From d7bcca217044e5395765e6c32e8ad332572e599c Mon Sep 17 00:00:00 2001 From: WilliamK112 <164879897+WilliamK112@users.noreply.github.com> Date: Tue, 8 Sep 2026 18:27:48 -0500 Subject: [PATCH 1/3] docs(security): clarify insecure transport policy --- docs/src/content/docs/enterprise/security.md | 32 +++++++++++++++++++- tests/unit/test_tls_docs_scope.py | 12 ++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/src/content/docs/enterprise/security.md b/docs/src/content/docs/enterprise/security.md index 7018ae6406..8201eff15e 100644 --- a/docs/src/content/docs/enterprise/security.md +++ b/docs/src/content/docs/enterprise/security.md @@ -565,9 +565,39 @@ 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- and host-blind canonical package names, so + they restrict which packages may install but cannot distinguish `http://` + from `https://` for the same package. 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 7db838bea2..fbb6dbe582 100644 --- a/tests/unit/test_tls_docs_scope.py +++ b/tests/unit/test_tls_docs_scope.py @@ -113,6 +113,18 @@ 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") + normalized = " ".join(security.split()) + + assert "no dedicated `allow_insecure` field" in normalized + assert "scheme- and host-blind canonical package names" in normalized + assert "does **not** enforce HTTPS" in normalized + assert "`registry_source.allow_non_registry`" in normalized + + def test_ssl_docs_verify_apm_path_and_mark_planned_scope(): docs = ( _repo_root() / "docs" / "src" / "content" / "docs" / "troubleshooting" / "ssl-issues.md" From b5e68a3f6c0056a1033a351e6d9cf98bc307e867 Mon Sep 17 00:00:00 2001 From: WilliamK112 <164879897+WilliamK112@users.noreply.github.com> Date: Tue, 8 Sep 2026 18:33:21 -0500 Subject: [PATCH 2/3] test(docs): make transport policy guard semantic --- tests/unit/test_tls_docs_scope.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_tls_docs_scope.py b/tests/unit/test_tls_docs_scope.py index fbb6dbe582..d3ffcffa72 100644 --- a/tests/unit/test_tls_docs_scope.py +++ b/tests/unit/test_tls_docs_scope.py @@ -117,12 +117,14 @@ 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") - normalized = " ".join(security.split()) + # 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- and host-blind canonical package names" in normalized - assert "does **not** enforce HTTPS" 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(): From 7f87ac1bb5f9df6471f3a9a318ff187a8ee43dd8 Mon Sep 17 00:00:00 2001 From: WilliamK112 <164879897+WilliamK112@users.noreply.github.com> Date: Tue, 8 Sep 2026 18:45:33 -0500 Subject: [PATCH 3/3] docs(security): preserve canonical host semantics --- docs/src/content/docs/enterprise/security.md | 7 ++++--- tests/unit/test_tls_docs_scope.py | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/src/content/docs/enterprise/security.md b/docs/src/content/docs/enterprise/security.md index 8201eff15e..bccb6d7658 100644 --- a/docs/src/content/docs/enterprise/security.md +++ b/docs/src/content/docs/enterprise/security.md @@ -567,9 +567,10 @@ For an org standardizing on APM: - Route dep traffic through an enterprise proxy with audit logging. See [Registry Proxy & Air-gapped](../registry-proxy/). - Treat insecure transport as a separate CI control. `apm-policy.yml` has no dedicated `allow_insecure` field: `dependencies.allow` and - `dependencies.deny` match scheme- and host-blind canonical package names, so - they restrict which packages may install but cannot distinguish `http://` - from `https://` for the same package. Reject committed + `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 diff --git a/tests/unit/test_tls_docs_scope.py b/tests/unit/test_tls_docs_scope.py index d3ffcffa72..a30741aa6a 100644 --- a/tests/unit/test_tls_docs_scope.py +++ b/tests/unit/test_tls_docs_scope.py @@ -121,7 +121,9 @@ def test_enterprise_security_docs_do_not_claim_transport_aware_policy(): normalized = " ".join(security.replace("**", "").split()) assert "no dedicated `allow_insecure` field" in normalized - assert "scheme- and host-blind canonical package names" 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