feat: add cargo, go, apt, and ghcr policy presets - #535
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✅ Files skipped from review due to trivial changes (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds four new network policy presets (apt, cargo, go, ghcr) specifying HTTPS endpoints and restricted binaries, and updates tests to include and validate these new presets' endpoints. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
test/policies.test.js (1)
120-150: Extend new preset tests to validate policy strictness, not only host presence.These cases currently verify endpoints but not security-critical fields (
access, optionalrules,binaries). Adding assertions for those fields will better protect against accidental policy broadening.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/policies.test.js` around lines 120 - 150, Extend each preset test to assert the loaded preset's security fields in addition to endpoints: after calling policies.loadPreset(...) and storing it in content, assert that content.access === "deny", assert that content.binaries is either falsy or an empty array, and assert that content.rules is either undefined or a non-empty array (to ensure rules are explicitly specified rather than permissive). Use the existing symbols policies.loadPreset and the content variable to locate and update the tests for "cargo", "go", "apt", and "ghcr".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@nemoclaw-blueprint/policies/presets/ghcr.yaml`:
- Around line 12-14: The ghcr preset is missing the standard policy fields and
endpoint-level rules used by other presets; update the host entry (host:
ghcr.io) to include protocol (e.g., https), enforcement (e.g., allow/deny or
"enforcement: enforced"), tls settings (e.g., tls: true or a tls object) and a
rules array; follow the established pattern from docker.yaml/pypi.yaml by adding
a rules entry for the endpoint (use a broad path like /** to match the current
codebase convention or add documented GHCR-specific paths such as /v2/** and
/token if you intend tighter constraints) so the file matches the other preset
schemas and runtime expectations.
In `@nemoclaw-blueprint/policies/presets/go.yaml`:
- Around line 19-21: The go preset currently allows full access to
storage.googleapis.com; update the go_proxy network policy to use path-based
rules and the modern policy fields: replace the unrestricted entry with an
endpoint for storage.googleapis.com that sets protocol: rest, enforcement:
enforce, tls: terminate and a rules block allowing only GET on the Go proxy path
(e.g. path: "/proxy-golang-org-prod/**"); also ensure proxy.golang.org and
sum.golang.org endpoints use protocol: rest, enforcement: enforce, tls:
terminate with rules allowing GET on "/**", and include the binaries list (e.g.
/usr/local/bin/openclaw and /usr/local/go/bin/go) under go_proxy.
---
Nitpick comments:
In `@test/policies.test.js`:
- Around line 120-150: Extend each preset test to assert the loaded preset's
security fields in addition to endpoints: after calling policies.loadPreset(...)
and storing it in content, assert that content.access === "deny", assert that
content.binaries is either falsy or an empty array, and assert that
content.rules is either undefined or a non-empty array (to ensure rules are
explicitly specified rather than permissive). Use the existing symbols
policies.loadPreset and the content variable to locate and update the tests for
"cargo", "go", "apt", and "ghcr".
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ff90e8a4-0663-43d1-b642-68a763f9e603
📒 Files selected for processing (5)
nemoclaw-blueprint/policies/presets/apt.yamlnemoclaw-blueprint/policies/presets/cargo.yamlnemoclaw-blueprint/policies/presets/ghcr.yamlnemoclaw-blueprint/policies/presets/go.yamltest/policies.test.js
| - host: ghcr.io | ||
| port: 443 | ||
| access: full |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For GHCR (ghcr.io), what HTTP methods and path patterns are required for image pull/push and token exchange, and can these be safely constrained to specific OCI paths (for example /v2/**)?
💡 Result:
Image pull (read)
Required path patterns (all under https://ghcr.io/v2/…) and typical HTTP methods:
GET|HEAD /v2/(API v2 “ping”) [1]GET|HEAD /v2/<name>/manifests/<reference>(manifest by tag or digest) [1]GET|HEAD /v2/<name>/blobs/<digest>(layer/config blobs) [1]
Image push (write)
Required path patterns (all under https://ghcr.io/v2/…) and typical HTTP methods:
POST /v2/<name>/blobs/uploads/(start upload) [1]PATCH /v2/<name>/blobs/uploads/<uuid>(upload chunk) [1]PUT /v2/<name>/blobs/uploads/<uuid>?digest=<digest>(finish upload) [1]GET /v2/<name>/blobs/uploads/<uuid>(optional: query upload status) [1]DELETE /v2/<name>/blobs/uploads/<uuid>(optional: cancel upload) [1]PUT /v2/<name>/manifests/<reference>(push manifest / index) [1]
Token exchange (Bearer token)
GHCR’s Bearer auth challenge commonly points clients to a non-/v2/ endpoint:
GET /token?service=ghcr.io&scope=repository:<name>:pull(and/or:push) (exact query varies) [2][3]- The registry’s
WWW-Authenticatechallenge may includeBearer realm="https://ghcr.io/token", service="ghcr.io", scope="repository:<name>:pull"[2]
Can you safely constrain to specific OCI paths like /v2/**?
- You can generally constrain image content operations (pull/push of manifests/blobs/uploads) to
ghcr.io/v2/**(plus query strings) because the distribution API routes are all under/v2/…[1]. - You cannot constrain everything to
/v2/**if you want auth to work, because the token exchange endpoint is/token(not/v2/...) and is referenced by the auth challenge (realm=.../token) [2][3].
Sources: [1] Docker Registry HTTP API V2 spec (CNCF Distribution); [2] example WWW-Authenticate header showing realm="https://ghcr.io/token"; [3] example request to https://ghcr.io/token?...service=ghcr.io.
🏁 Script executed:
# First, let's see the full ghcr.yaml file to understand its structure
cat -n nemoclaw-blueprint/policies/presets/ghcr.yamlRepository: NVIDIA/NemoClaw
Length of output: 612
🏁 Script executed:
# Let's also look for other preset files to see examples of endpoint-level rules
find nemoclaw-blueprint/policies/presets -name "*.yaml" -type f | head -10Repository: NVIDIA/NemoClaw
Length of output: 530
🏁 Script executed:
# Check if any other presets have rules defined to understand the syntax
rg "rules:" nemoclaw-blueprint/policies/presets/ -A 5 -B 2 --max-count=10Repository: NVIDIA/NemoClaw
Length of output: 13143
Add endpoint-level rules and standard policy fields to match other presets.
The configuration is missing required fields: protocol, enforcement, tls, and rules that are present in all other preset files (docker.yaml, pypi.yaml, etc.). Add these fields to follow the established pattern. While endpoint-level rules are fully supported (see docker.yaml for examples), note that the current codebase pattern uses broad path rules like /** rather than tight constraints. If GHCR-specific paths like /v2/** and /token are intended, they should be documented in the rules.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@nemoclaw-blueprint/policies/presets/ghcr.yaml` around lines 12 - 14, The ghcr
preset is missing the standard policy fields and endpoint-level rules used by
other presets; update the host entry (host: ghcr.io) to include protocol (e.g.,
https), enforcement (e.g., allow/deny or "enforcement: enforced"), tls settings
(e.g., tls: true or a tls object) and a rules array; follow the established
pattern from docker.yaml/pypi.yaml by adding a rules entry for the endpoint (use
a broad path like /** to match the current codebase convention or add documented
GHCR-specific paths such as /v2/** and /token if you intend tighter constraints)
so the file matches the other preset schemas and runtime expectations.
There was a problem hiding this comment.
These presets intentionally use access: full rather than tls: terminate with rules. Container registries (like package managers) use CONNECT tunneling for pulls, and tls: terminate breaks that — the proxy can't inspect the traffic without MITM'ing the TLS session, which causes auth failures and connection drops.
This is the same issue that broke the npm and pypi presets on main. ericksoa's #356 is actively ripping out tls: terminate from those presets and replacing with access: full for exactly this reason. GHCR has the same tunneling behavior so the same pattern applies here.
The REST API presets (discord, slack, jira, etc.) can use tls: terminate because they're plain HTTPS request/response — no CONNECT tunneling involved.
There was a problem hiding this comment.
@brianwtaylor, thanks for the detailed explanation — that makes the design intent clear and my concern was mistaken. The distinction between CONNECT-tunneling clients (container registries, package managers) and plain HTTPS REST API clients is the right model here, and access: full is correct for GHCR for exactly the same reasons it's being applied to npm/pypi in #356.
✏️ Learnings added
Learnt from: brianwtaylor
Repo: NVIDIA/NemoClaw PR: 535
File: nemoclaw-blueprint/policies/presets/ghcr.yaml:12-14
Timestamp: 2026-03-21T03:32:06.240Z
Learning: In the NemoClaw project (nemoclaw-blueprint/policies/presets/), package manager and container registry presets (e.g., cargo, go, apt, ghcr, npm, pypi) intentionally use `access: full` instead of `tls: terminate` with rules. These clients use CONNECT tunneling for their traffic, and `tls: terminate` would require MITM'ing the TLS session, causing auth failures and connection drops. `tls: terminate` with rules is only appropriate for plain HTTPS request/response REST API presets (e.g., discord, slack, jira). PR `#356` is actively migrating npm/pypi presets from `tls: terminate` to `access: full` for this reason.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| - host: storage.googleapis.com | ||
| port: 443 | ||
| access: full |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For Go module downloads via proxy.golang.org, which path prefixes on storage.googleapis.com are used for module artifacts, and can access be restricted to those prefixes?
💡 Result:
For module artifact blobs served via proxy.golang.org, the Go tool is typically redirected (HTTP 302) to Google Cloud Storage at the bucket/path:
https://storage.googleapis.com/proxy-golang-org-prod/<object>(same content is also accessible via the virtual-host formhttps://proxy-golang-org-prod.storage.googleapis.com/<object>). The objects are the cached module files like*.zip,*.mod,*.info(and related hash files), stored as flat object names under that bucket (no stable per-module “directory” hierarchy beyond the object name itself). [1][2]
Can access be restricted to those prefixes?
- Yes, but only if your egress control can filter by full URL path (usually meaning an explicit HTTP(S) proxy / secure web gateway doing URL filtering, often requiring TLS interception to see the path).
- No, not with a plain IP/hostname allowlist firewall: with normal HTTPS, devices on-path only reliably see the destination hostname (SNI) and not the full URI path, so they can’t enforce “only allow
/proxy-golang-org-prod/...” without deeper inspection. [3]
Sources:
[1] GitHub allowlist reference listing storage.googleapis.com/proxy-golang-org-prod/
[2] Example redirect from proxy.golang.org to https://storage.googleapis.com/proxy-golang-org-prod/...
[3] Note that in HTTPS “all traffic is encrypted including the URI path after the hostname” (i.e., path-based filtering needs inspection)
🏁 Script executed:
# First, find and examine the go.yaml file
find . -name "go.yaml" -o -name "go.yml" 2>/dev/null | head -20Repository: NVIDIA/NemoClaw
Length of output: 104
🏁 Script executed:
# Also check if nemoclaw-blueprint is defined in the repo and what it is
find . -name "*nemoclaw*" -o -name "*blueprint*" 2>/dev/null | head -20Repository: NVIDIA/NemoClaw
Length of output: 197
🏁 Script executed:
# Check the structure and broader context of the policies directory
ls -la nemoclaw-blueprint/policies/presets/ 2>/dev/null || find . -path "*/policies/presets/*" -type f 2>/dev/null | head -20Repository: NVIDIA/NemoClaw
Length of output: 954
🏁 Script executed:
# Read the go.yaml file to see the full policy structure
cat -n nemoclaw-blueprint/policies/presets/go.yamlRepository: NVIDIA/NemoClaw
Length of output: 873
🏁 Script executed:
# Check the main blueprint.yaml to understand the tool and its capabilities
cat -n nemoclaw-blueprint/blueprint.yamlRepository: NVIDIA/NemoClaw
Length of output: 2264
🏁 Script executed:
# Look for documentation on policy structure and capabilities
find . -name "README*" -o -name "*.md" 2>/dev/null | xargs grep -l "storage.googleapis.com\|path.*prefix\|access.*full" 2>/dev/null | head -10Repository: NVIDIA/NemoClaw
Length of output: 70
🏁 Script executed:
# Check if there are examples of path-based filtering in other policy files
grep -r "path:" nemoclaw-blueprint/policies/presets/ 2>/dev/null | head -20Repository: NVIDIA/NemoClaw
Length of output: 1909
🏁 Script executed:
# Look at the discord.yaml and docker.yaml examples to understand how path-based filtering is implemented
cat -n nemoclaw-blueprint/policies/presets/discord.yamlRepository: NVIDIA/NemoClaw
Length of output: 1233
🏁 Script executed:
# Check docker.yaml as well for comparison
cat -n nemoclaw-blueprint/policies/presets/docker.yamlRepository: NVIDIA/NemoClaw
Length of output: 1605
🏁 Script executed:
# Search for documentation on how the OpenClaw/network policy system works
find . -name "*.md" -o -name "*.txt" 2>/dev/null | xargs grep -l "network_polic\|access.*full\|path.*filter" 2>/dev/null | head -10Repository: NVIDIA/NemoClaw
Length of output: 236
🏁 Script executed:
# Look for any policy schema or documentation files
find . -name "*policy*" -o -name "*schema*" 2>/dev/null | grep -E "\.(yaml|md|txt|json)$" | head -20Repository: NVIDIA/NemoClaw
Length of output: 108
🏁 Script executed:
# Read the network policy customization documentation
cat -n docs/network-policy/customize-network-policy.mdRepository: NVIDIA/NemoClaw
Length of output: 3517
🏁 Script executed:
# Check the network policy reference documentation
cat -n docs/reference/network-policies.mdRepository: NVIDIA/NemoClaw
Length of output: 4805
🏁 Script executed:
# Check if there are other examples of storage.googleapis.com or similar services using rules
grep -r "storage.googleapis.com\|path.*proxy-golang-org" . 2>/dev/null | head -20Repository: NVIDIA/NemoClaw
Length of output: 224
🏁 Script executed:
# Check the test file to see if there are any other clues about the go.yaml policy
cat -n test/policies.test.js | head -50Repository: NVIDIA/NemoClaw
Length of output: 2237
🏁 Script executed:
# Look for other examples in the codebase where storage.googleapis.com or GCS is used
grep -r "proxy-golang-org-prod\|storage.googleapis" . --include="*.yaml" --include="*.yml" --include="*.md" 2>/dev/nullRepository: NVIDIA/NemoClaw
Length of output: 141
🏁 Script executed:
# Check if pypi.yaml might have similar patterns since it also downloads packages
cat -n nemoclaw-blueprint/policies/presets/pypi.yamlRepository: NVIDIA/NemoClaw
Length of output: 873
🏁 Script executed:
# Check if proxy.golang.org and sum.golang.org also need path-based filtering
# Let's see what paths these services use
grep -A5 "proxy.golang.org\|sum.golang.org" nemoclaw-blueprint/policies/presets/go.yamlRepository: NVIDIA/NemoClaw
Length of output: 312
🏁 Script executed:
# Let's see the test to understand what hosts are expected from go preset
cat -n test/policies.test.js | grep -A20 "go"Repository: NVIDIA/NemoClaw
Length of output: 2402
Update the go preset to use path-based rules for storage.googleapis.com and align with the modern policy format.
Lines 19–21 allow unrestricted access to storage.googleapis.com, which hosts Go module archives at a specific path prefix. NemoClaw supports path-based filtering through TLS termination and rules (as shown in discord.yaml and docker.yaml presets). Restrict access to the Go proxy path prefix and update the entire go_proxy policy to follow the pattern of other presets with protocol: rest, enforcement: enforce, tls: terminate, and path-based rules:
Example structure:
network_policies:
go_proxy:
name: go_proxy
endpoints:
- host: proxy.golang.org
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: GET, path: "/**" }
- host: sum.golang.org
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: GET, path: "/**" }
- host: storage.googleapis.com
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: GET, path: "/proxy-golang-org-prod/**" }
binaries:
- { path: /usr/local/bin/openclaw }
- { path: /usr/local/go/bin/go }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@nemoclaw-blueprint/policies/presets/go.yaml` around lines 19 - 21, The go
preset currently allows full access to storage.googleapis.com; update the
go_proxy network policy to use path-based rules and the modern policy fields:
replace the unrestricted entry with an endpoint for storage.googleapis.com that
sets protocol: rest, enforcement: enforce, tls: terminate and a rules block
allowing only GET on the Go proxy path (e.g. path: "/proxy-golang-org-prod/**");
also ensure proxy.golang.org and sum.golang.org endpoints use protocol: rest,
enforcement: enforce, tls: terminate with rules allowing GET on "/**", and
include the binaries list (e.g. /usr/local/bin/openclaw and
/usr/local/go/bin/go) under go_proxy.
There was a problem hiding this comment.
Same reasoning as the ghcr comment above — go get and go mod download rely on CONNECT tunneling through the proxy, so tls: terminate would break module downloads. The path restriction idea for storage.googleapis.com is clever but it requires the proxy to terminate TLS to see the path, which is the thing that breaks it.
The access: full pattern is what ericksoa is moving the package-manager presets to in #356 (npm and pypi both had this exact problem on main). These new presets follow that same convention from the start so we don't repeat the breakage.
I hear the concern about storage.googleapis.com being overly broad — but in practice the sandbox's network policy only applies inside the container, and the binary allowlist (go, openclaw) already limits what process can make the request.
There was a problem hiding this comment.
@brianwtaylor, thanks for the thorough explanation — that's a clear and valid constraint. CONNECT tunneling by go get/go mod download means TLS termination would break the flow entirely, so path-based filtering on storage.googleapis.com isn't viable here regardless of how precise it would be. The access: full + binary allowlist layering makes sense as the correct approach, and it's good that these new presets align with the direction in #356 from the start rather than inheriting the same breakage that hit npm and pypi.
I'll retract the suggestion.
✏️ Learnings added
Learnt from: brianwtaylor
Repo: NVIDIA/NemoClaw PR: 535
File: nemoclaw-blueprint/policies/presets/go.yaml:19-21
Timestamp: 2026-03-21T03:32:16.833Z
Learning: In NemoClaw, package manager presets for tools that rely on CONNECT tunneling (e.g., Go's `go get`/`go mod download`, npm, pypi) must use `access: full` rather than `protocol: rest` + `tls: terminate` + path-based rules. TLS termination breaks CONNECT-based proxying. The intended security layering is the `binaries` allowlist (restricting which process can reach allowed endpoints) rather than path filtering. This pattern is being standardized in PR `#356` for all package-manager presets.
Learnt from: brianwtaylor
Repo: NVIDIA/NemoClaw PR: 535
File: nemoclaw-blueprint/policies/presets/go.yaml:19-21
Timestamp: 2026-03-21T03:32:16.833Z
Learning: In the NemoClaw go preset (`nemoclaw-blueprint/policies/presets/go.yaml`), `storage.googleapis.com` is intentionally allowed with `access: full` (no path restriction) because Go module downloads are proxied via CONNECT tunneling and TLS termination cannot be used to inspect paths. The binary allowlist (`/usr/local/go/bin/go`, `/usr/local/bin/openclaw`) is the security boundary, not path-based filtering.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
e8d75cd to
3d55656
Compare
Add network policy presets for Rust Cargo, Go modules, Debian/Ubuntu apt repositories, and GitHub Container Registry. Each follows the existing YAML schema with access: full and binary restrictions, consistent with upstream direction (PR NVIDIA#356). Signed-off-by: Brian Taylor <brian.taylor818@gmail.com>
3d55656 to
76edbb9
Compare
|
Thanks for the proposed addition of new policy presets for Cargo, Go, apt, and GHCR, which could help improve the security and flexibility of NemoClaw for users working with different package managers. |
Summary
access: fullwith binary restrictions, consistent with the upstream direction for package manager presets (see fix: repair pypi and npm policy presets for package manager traffic #356)Related to #19 — expanding preset coverage so additional package managers work inside the sandbox out of the box
Motivation
The sandbox ships with presets for npm, PyPI, Docker Hub, and a few messaging services, but developers using Rust, Go, Debian-based system packages, or GitHub Container Registry have no preset available. These 4 presets cover the most common gaps.
Test plan
Automated Tests
Summary by CodeRabbit
New Features
Tests