Skip to content

security(admission): enforce credential-file access boundary #246

Closed

Description

@seonghobae

Refs #128 and Draft #129.

Valid finding

Fresh inspection of Agent Artifact Admission exact head 63231dcf493ff3e5f53630ffd04016299358021d found that crates/agent-artifact-admission/src/config.rs::load_admin_token() validates bounded JSON and token contents but read_bounded() opens the credential path without enforcing host filesystem access permissions. The shipped runbook simultaneously requires the administrator-token credential file to have restrictive filesystem permissions and calls it a protected local secret.

This leaves a gap between documented deployment policy and executable startup acceptance. On a shared Unix host, a group/world-readable credential file can expose the bearer token to an unintended local principal, and a group/world-writable credential file can permit unauthorized replacement of security-relevant authentication material. Because the admission service is loopback-only, same-host credential exposure is directly relevant: possession of the token authorizes /v1/policy and /v1/admissions.

MITRE CWE-732 (Version 4.20) explicitly treats overly broad access to security-critical configuration/credential resources as a confidentiality/integrity weakness and recommends checking insecure permissions and failing startup rather than relying on operators to tighten them manually: https://cwe.mitre.org/data/definitions/732.html

NIST SP 800-53 Rev. 5, AC-6/AC-6(1), requires least privilege and explicit authorization to security functions and security-relevant information; NIST's current publication page records Release 5.2.0 as the latest minor release: https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final

Ownership boundary

This is Wardnet-local credential admission for the standalone Agent Artifact Admission process. It does not create a secret-management backend, duplicate Keyverse, implement quarantine/runtime isolation, alter EgressWeave transport authority, or change contextual-orchestrator/AppGuardrail ownership. Long-lived enterprise secret distribution remains an external/canonical secret-management concern; Wardnet must still fail closed when it is explicitly given a local credential file whose access boundary contradicts its own deployment contract.

RED acceptance

Do not mutate #129 while its current uv hash-mode test-only head is awaiting exact hosted execution. After that lineage settles, create a fresh test-first successor and require on Unix:

  • an otherwise valid credential file with any group/other read, write, or execute permission is rejected before listener bind;
  • owner-only 0600 and read-only 0400 credential files remain valid positive controls;
  • the error is the existing non-secret ConfigError::InvalidCredential (or an equally bounded typed credential-access error) and does not expose the token or path contents;
  • the test proves the failure through load_admin_token and the CLI/startup boundary, not by source-string inspection.

Windows ACL semantics are not to be approximated with POSIX mode bits. If the supported Windows deployment cannot obtain an equivalent trustworthy ACL predicate through the standard/runtime platform APIs, keep that platform fail-closed for this local-file credential mode until a separately evidenced ACL adapter exists; do not silently claim parity.

Symlink/TOCTOU ownership is a separate finding unless a concrete exploit/reproduction proves it should be solved in the same causal slice.

Minimum repair boundary

Only after a semantic RED: validate permissions from the already opened credential file's metadata so the decision is bound to the handle actually read; on Unix reject group/other permission bits before parsing/returning token material. Keep config-policy/audit-path permissions out of this slice unless their own hostile tests demonstrate a distinct requirement. Update the runbook/CHANGELOG and trace CWE-732 + NIST AC-6 to executable evidence.

Completion gate

Close only when an exact unchanged candidate has hostile RED lineage, minimum causal source repair, cargo fmt --check, locked workspace tests, strict Clippy, then-live security/review/coverage/SBOM/provenance gates, fresh protected-base compatibility, and reaches protected main or a verified successor carries every valid delta. No self/model approval, routine administrator bypass, force update, gate weakening, or predecessor-evidence transfer.

Activity

  1. seonghobae commented on Sep 10, 2026

    @seonghobae
    ContributorAuthor

    Completion receipt: PR #247 exact 2efe188cd94b77d039c749368cfeee977dbb51e4 carried the complete #246 credential-file access contract. Hosted semantic RED d9126dba9fad4332eec16cb441d3fb7a569452b4 proved 0640 credential material was accepted after the preceding suite passed. The minimum repair opens the credential path once, validates metadata on that same handle before materializing secret bytes, rejects any Unix group/other permission bit (mode & 0o077 != 0) through the existing non-secret InvalidCredential outcome, preserves owner-only 0600/0400, and fails the local-file boundary closed where equivalent POSIX permission semantics are unavailable. Exact CI 34436046500 and Fuzz 34436046509 are terminal SUCCESS and PR #247 has zero review threads. #247 was ordinarily merged into canonical #129 as 473f689fa6fd0c9662e4709e5d6257715371bf36; fresh ancestry comparison proves that merge is an ancestor of current #129 c4030f7fecaa5627ccec31ce58eca7e59db45334 (ahead 17 / behind 0). The full implementation/test/evidence delta is therefore retained by the canonical successor lineage; no force update, gate weakening, protected-main or release claim.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions