Skip to content

bug(cli): policy update --add-endpoint rejects allow_encoded_slash, the same option its denial message tells users to set #4926

Open

Description

@youyouhe

User Story

I use OpenShell to run npm install inside sandboxes. Scoped packages make the registry request-target contain %2F (/registry.npmjs.org/@types%2Fnode), and OpenShell's L7 parser rejects that target unless the endpoint sets allow_encoded_slash: true.

I directly encountered that this option can only be set by pushing a whole policy document. The incremental openshell policy update --add-endpoint path rejects it — and the rejection text is the same message that tells users to configure the option in the first place.

Problem Statement

With an endpoint allowed but without the option, a scoped install fails closed with:

NET:OPEN [MED] DENIED registry.npmjs.org:443 [policy:dev_registries engine:http-parser]
  [reason:HTTP request-target rejected: request-target contains an encoded '/' (%2F)
   which is not allowed on this endpoint; passthrough credential relay uses strict path
   parsing, so configure this endpoint with protocol: rest and allow_encoded_slash: true]

Following that advice from the CLI fails:

$ openshell policy update <sandbox> --rule-name enc_test --binary /usr/bin/curl \
    --add-endpoint "registry.npmjs.org:443:full:rest:enforce:allow_encoded_slash=true" --dry-run
Error: --add-endpoint options segment supports only 'allow-uninspected-credentials',
'websocket-credential-rewrite', 'request-body-credential-rewrite', and
'allowed-ip=<CIDR-or-IP>'; got 'allow_encoded_slash=true' in
'registry.npmjs.org:443:full:rest:enforce:allow_encoded_slash=true'

The identical endpoint without the option passes validation (--dry-run renders the merged policy), so the field itself is the only difference.

The same rule defined inside a full policy document and applied with openshell policy set works: scoped installs succeed afterwards and the audit shows the encoded target as allowed:

HTTP:GET [INFO] ALLOWED GET https://registry.npmjs.org/@types%2Fnode

Impact / Why This Matters

  • The documented remediation for %2F targets is unreachable through the incremental CLI, which is the natural tool for changing one rule on a live sandbox. Operators must replace the entire policy document to grant a single encoded-path permission, i.e. a much larger change surface than adding a rule.
  • The error message contradicts itself: it directs the user to set an option that the same subcommand refuses.
  • Scoped npm and GitLab-style (group%2Fproject) dependencies cannot be enabled incrementally at all, so any automated "approve the denial, retry" workflow is blocked for this class of target and requires a manual full-policy push instead.

Acceptance Criteria

  • openshell policy update --add-endpoint accepts the encoded-slash option (whatever spelling is chosen, e.g. allow-encoded-slash / allow_encoded_slash=true), and --dry-run prints the field in the merged endpoint.
  • After applying that update to a live sandbox, a request-target containing %2F is allowed at the L7 layer and audit shows HTTP:* ALLOWED rather than an engine:http-parser denial.
  • The "supported options" error either lists the field or names a working alternative command for setting it.

Reproduction Steps

  1. openshell sandbox create --name enc -- sh -c 'sleep 900' (default policy).
  2. Inside the sandbox: npm install @types/node → fails with the %2F denial above.
  3. On the host: run the policy update --add-endpoint ...:allow_encoded_slash=true --dry-run command → rejected by the options whitelist.
  4. Same endpoint without the option → --dry-run succeeds.
  5. Create a full policy YAML with the endpoint set to protocol: rest, access: full, allow_encoded_slash: true, apply with openshell policy set, then repeat step 2 → install succeeds.

Suggested UX

Extend the --add-endpoint options whitelist with the encoded-slash flag (matching the schema field name), or allow --add-endpoint to take a YAML/JSON endpoint object so every schema field is expressible incrementally:

$ openshell policy update sb1 --rule-name npm --binary /usr/local/node/bin/node \
    --add-endpoint "registry.npmjs.org:443:full:rest:enforce:allow-encoded-slash" --wait

Environment

Activity

  1. youyouhe commented on Oct 26, 2026

    @youyouhe
    Author

    Additional evidence and environment

    Environment

    Item Value
    OpenShell CLI / gateway / supervisor / sandbox runtime all 0.1.5
    Gateway local, mTLS, gateway info → Status: healthy, compute driver docker 0.1.5 (protocol 1.0)
    Docker 29.9.2 (build a82e528), cgroup=systemd/2, runtime runc
    Host Windows 10 Pro 22H2, build 19045.3803, WSL 2.9.13
    Guest Ubuntu 24.04.3 LTS, kernel 6.18.54.1-microsoft-standard-WSL2, 12 vCPU, 10 GiB
    Sandbox image openshell-dev-all:1.2 (Ubuntu 24.04 + Node.js 22.14.0 / npm 10.9.2)
    Runtime images ghcr.io/nvidia/openshell/sandbox:0.1.5, ghcr.io/nvidia/openshell/supervisor:0.1.5

    Full transcript

    Step 1 — endpoint declared as protocol: tcp (the shape an operator naturally writes first)

    network_policies:
      npm_tcp_only:
        endpoints:
          - {host: registry.npmjs.org, port: 443, protocol: tcp}
        binaries:
          - path: "/**"

    Step 2 — scoped install fails; audit line (complete, untruncated in the source log before the trailing ellipsis):

    [1793022756.087] [sandbox] [OCSF ] [ocsf] NET:OPEN [MED] DENIED registry.npmjs.org:443
      [policy:npm_tcp_only engine:http-parser]
      [reason:HTTP request-target rejected: request-target contains an encoded '/' (%2F) which is not
       allowed on this endpoint; passthrough credential relay uses strict path parsing, so configure
       this endpoint with protocol: rest and allow_encoded_slash: true for encoded-path support]
    

    Step 3 — the incremental CLI cannot express the fix its own message recommends:

    $ openshell policy update ev4926 --rule-name enc_probe --binary /usr/bin/curl \
        --add-endpoint "registry.npmjs.org:443:full:rest:enforce:allow_encoded_slash=true" --dry-run
    Error:   × --add-endpoint options segment supports only 'allow-uninspected-
      │ credentials', 'websocket-credential-rewrite', 'request-body-credential-
      │ rewrite', and 'allowed-ip=<CIDR-or-IP>'; got 'allow_encoded_slash=true' in
      │ 'registry.npmjs.org:443:full:rest:enforce:allow_encoded_slash=true'

    Step 4 — control: identical endpoint minus the option passes validation and renders in --dry-run:

          enforcement: enforce
          access: full
      binaries:
        - path: /usr/bin/curl
    npm_tcp_only:
      name: npm_tcp_only
      endpoints:
        - host: registry.npmjs.org
          port: 443
          protocol: tcp

    Step 5 — same rule delivered inside a full document is accepted:

    $ openshell policy set --policy policy-npm-rest.yaml ev4926
    ✓ Policy version 2 submitted (hash: b372cc773fc4)
    network_policies:
      npm_rest:
        endpoints:
          - host: registry.npmjs.org
            port: 443
            protocol: rest
            enforcement: enforce
            access: full
            allow_encoded_slash: true

    Step 6 — installs now succeed, two different scoped packages:

    @types/node 26.6.5
    @types/lodash 4.17.27

    Step 7 — audit shows exactly the previously rejected target allowed at the L7 engine:

    [1793022927.743] [sandbox] [OCSF] HTTP:GET [INFO] ALLOWED GET https://registry.npmjs.org/@types%2Fnode   [policy:npm_rest engine:l7]
    [1793022931.482] [sandbox] [OCSF] HTTP:GET [INFO] ALLOWED GET https://registry.npmjs.org/@types%2Flodash [policy:npm_rest engine:l7]
    

    Conclusion from the evidence

    Runtime behaviour is correct once the field reaches the supervisor. The only blocker is the option whitelist in policy update --add-endpoint: there is no supported incremental way to set a schema field that the denial message instructs the user to set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions