diff --git a/docs/src/content/docs/enterprise/security.md b/docs/src/content/docs/enterprise/security.md index 7018ae640..bccb6d765 100644 --- a/docs/src/content/docs/enterprise/security.md +++ b/docs/src/content/docs/enterprise/security.md @@ -565,9 +565,40 @@ For an org standardizing on APM: - Publish an `apm-policy.yml` from your `/.github` repo with an allow list and an MCP transport restriction. See [Governance Guide](../governance-guide/). - Require signed commits on the source repos APM pulls from -- this is where the trust chain bottoms out. - Route dep traffic through an enterprise proxy with audit logging. See [Registry Proxy & Air-gapped](../registry-proxy/). -- Forbid `allow_insecure: true` via the policy allow list, except where an air-gapped mirror demands it. +- Treat insecure transport as a separate CI control. `apm-policy.yml` has no + dedicated `allow_insecure` field: `dependencies.allow` and + `dependencies.deny` match scheme-blind canonical package identities. The + default `github.com` host is omitted while non-default hosts are retained, so + rules can restrict package and host identity but cannot distinguish + `http://` from `https://` for the same canonical host and path. Reject committed + `allow_insecure: true` entries and prohibit `--allow-insecure` and + `--allow-insecure-host` in standard CI; review both explicit gates for any + air-gapped exception. `registry_source.allow_non_registry` is a separate + source-routing control, not an insecure-transport setting. - Scan committed `apm.yml` for literal secrets in `mcp.env` values -- APM assumes env-var indirection (`GITHUB_TOKEN: ${GITHUB_TOKEN}`) but does not enforce it. `apm install` auto-adds `apm_modules/` to `.gitignore`, keeping cached source trees out of commits. +A restrictive dependency policy is still valuable, but it is identity-based, +not transport-aware: + +```yaml +# apm-policy.yml +name: contoso-security +version: "1.0" +enforcement: block + +dependencies: + allow: + - "contoso/approved-agent-config" + - "microsoft/*" +``` + +This example blocks every unlisted package identity regardless of transport; it +does **not** enforce HTTPS for the two allowed patterns. See the +[HTTP dependency two-gate model](#http-insecure-dependencies), +[dependency pattern matching](../policy-reference/#pattern-matching), and the +[`registry_source` policy](../../reference/policy-schema/#registry_source) for +the three distinct controls. + ## Frequently asked questions ### Can a package embed hidden instructions? diff --git a/tests/unit/test_tls_docs_scope.py b/tests/unit/test_tls_docs_scope.py index 7db838bea..a30741aa6 100644 --- a/tests/unit/test_tls_docs_scope.py +++ b/tests/unit/test_tls_docs_scope.py @@ -113,6 +113,22 @@ def test_enterprise_security_docs_transport_trust_model(): assert "Rust" in security +def test_enterprise_security_docs_do_not_claim_transport_aware_policy(): + security = ( + _repo_root() / "docs" / "src" / "content" / "docs" / "enterprise" / "security.md" + ).read_text(encoding="utf-8") + # Keep this contract about the guidance rather than Markdown presentation. + normalized = " ".join(security.replace("**", "").split()) + + assert "no dedicated `allow_insecure` field" in normalized + assert "scheme-blind canonical package identities" in normalized + assert "non-default hosts are retained" in normalized + assert "host-blind" not in normalized + assert "does not enforce HTTPS" in normalized + assert "`registry_source.allow_non_registry`" in normalized + assert "Forbid `allow_insecure: true` via the policy allow list" not in normalized + + def test_ssl_docs_verify_apm_path_and_mark_planned_scope(): docs = ( _repo_root() / "docs" / "src" / "content" / "docs" / "troubleshooting" / "ssl-issues.md"