Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8f70a29
fix(audit): bound the deploy-tree hidden-Unicode walk to primitive di…
lkshrk Sep 12, 2026
aae84fe
Merge branch 'main' into fix/audit-scan-primitive-roots
sergio-sisternes-epam Sep 27, 2026
cd91c2c
Merge remote-tracking branch 'origin/main' into danielmeppiel-pr-2962…
danielmeppiel Sep 30, 2026
42a66b5
fix(audit): separate canonical primitive discovery from prompt checks
danielmeppiel Sep 30, 2026
555c4bc
fix(audit): preserve user-scope claim metadata
danielmeppiel Sep 30, 2026
41f29e1
docs(hooks): reference canonical native layouts in module overview
danielmeppiel Sep 30, 2026
cf0a1d9
Merge current main before the revised audit panel
danielmeppiel Sep 30, 2026
df7e236
fix(audit): fold coverage and remediation panel findings
danielmeppiel Sep 30, 2026
9be8b8e
Merge remote-tracking branch 'origin/main' into danielmeppiel-pr-2962…
danielmeppiel Sep 30, 2026
58dafc0
fix(audit): close review and lifecycle gaps with bounded conformance …
danielmeppiel Sep 30, 2026
2460368
fix(audit): preserve user-scope protection and CI prompt provenance
danielmeppiel Sep 30, 2026
29f92b0
Merge current main before final audit verification
danielmeppiel Sep 30, 2026
66ba9ea
fix(audit): retain plain file labels for unstructured findings
danielmeppiel Sep 30, 2026
d317fd2
test(architecture): enforce shared audit finding serializers
danielmeppiel Sep 30, 2026
04cea96
test: cover audit owners in exhaustive mutation matrix
danielmeppiel Sep 30, 2026
2e349a6
Merge current main for audit exception qualification
danielmeppiel Sep 30, 2026
58bd983
Merge current main at final audit qualification checkpoint
danielmeppiel Sep 30, 2026
0aad2c1
test: cover marketplace remote owner in mutation matrix
danielmeppiel Sep 30, 2026
dc89f5f
Merge current main preserving audit qualification and MCP fixes
danielmeppiel Sep 30, 2026
78b8b5a
Merge current main preserving audit and native lifecycle authorities
danielmeppiel Sep 30, 2026
0cac809
test(lifecycle): reconcile native Claude compile expectations
danielmeppiel Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .apm/architecture/owners/hooks-integrations.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
{
"version": 1,
"owners": [
{
"id": "audit-finding-serialization",
"decision": "Ordinary and CI finding artifact, pointer and coordinate serialization",
"owner": "security/audit_report.py (finding_to_json, finding_to_sarif)",
"selectors": [
"src/apm_cli/security/audit_report.py",
"src/apm_cli/policy/models.py"
],
"guards": ["audit-finding-serialization"]
},
{
"id": "audit-primitive-discovery",
"decision": "Registry-derived deployed primitive discovery and applicable prompt coverage",
"owner": "security/primitive_discovery.py (primitive_surfaces), security/file_scanner.py (scan_project_result) through target skills_deploy_path and native hook contracts",
"selectors": [
"src/apm_cli/security/primitive_discovery.py",
"src/apm_cli/security/file_scanner.py"
],
"guards": ["audit-primitive-discovery"]
},
{
"id": "native-instruction-projection",
"decision": "Native instruction filename and rendered content projection shared by installation and per-source user-root coverage",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- `apm audit` discovers tracked and untracked target-native prompts without treating executable commands as prompt text; unreadable or unsupported recognized formats now fail with incomplete coverage, and shared/user configuration cannot be auto-stripped. Review named incomplete or protected locations manually before re-auditing. Proposed `specs/openapm-v0.1.md` audit contract remains subject to specification adoption. -- by @lkshrk (#2962)
- Cursor MCP configuration now preserves native runtime environment references and authored static values, normalizes scalar environment values, and omits null entries. (by @icecold009, #3070)
- Global skill installs work through `HOME`/`APM_HOME` directory aliases without relaxing package or destination safety; frozen installs reject ref, pin, provider and transport drift, and unseeded mutable refs resolve upstream. After an intentional declaration change, review it and run `apm install --update`. (by @DaveMeadAdjust, #2876)
- GitHub MCP authentication now preserves explicit `Authorization` headers and writes the selected environment-variable reference on runtime-capable targets instead of resolving the credential into generated config. (#3103)
Expand Down
44 changes: 42 additions & 2 deletions CONFORMANCE.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"target_capability_declaration": "MCPClientAdapter.supports_user_scope (OpenAPM Target Registry v0.1 implementation profile)"
},
"generator": "gen_statement.py v1",
"optional_features": {
"deployed-prompt-audit": "APM claims the proposed deployed-prompt-audit capability (req-pl-019, req-pl-020), subject to the pending Section 9.3 amendment process, not adopted-spec approval. APM's own Consumer deployment implementation supplies the definitions: project and user scopes follow TargetProfile deployment roots and PrimitiveMapping formats, including shared native hook settings via native_hook_config. The target-by-target locations, supported formats, prompt applicability and exclusions are published in docs/src/content/docs/reference/cli/audit.md. ContentScanner checks hidden or suspicious Unicode code points that may conceal instructions, not semantic prompt-injection detection; a clean result is not a guarantee that prompt text is safe. App SQLite state and unrelated transcripts are excluded. Detection does not grant execution, ownership or remediation authority."
},
"requirements": [
{
"conformance_class": "consumer",
Expand Down Expand Up @@ -843,6 +846,43 @@
"tests/spec_conformance/test_policy_reqs.py::test_policy_identity_casing_matches_dependency_source_semantics"
]
},
{
"conformance_class": "governance",
"id": "req-pl-019",
"keyword": "MUST",
"section": "6.8.1",
"status": "active",
"test_count": 14,
"tests": [
"tests/spec_conformance/test_policy_reqs.py::test_deployed_prompt_applicability_and_incomplete_exit[False]",
"tests/spec_conformance/test_policy_reqs.py::test_deployed_prompt_applicability_and_incomplete_exit[True]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[0-json-False]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[0-json-True]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[0-sarif-False]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[0-sarif-True]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[0-text-False]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[0-text-True]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[1-json-False]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[1-json-True]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[1-sarif-False]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[1-sarif-True]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[1-text-False]",
"tests/spec_conformance/test_policy_reqs.py::test_native_findings_identify_only_the_offending_field[1-text-True]"
]
},
{
"conformance_class": "governance",
"id": "req-pl-020",
"keyword": "MUST",
"section": "6.8.1",
"status": "active",
"test_count": 3,
"tests": [
"tests/spec_conformance/test_policy_reqs.py::test_deployed_prompt_protected_remediation_is_atomic",
"tests/spec_conformance/test_policy_reqs.py::test_static_user_scope_refuses_automatic_strip_in_real_cli[False]",
"tests/spec_conformance/test_policy_reqs.py::test_static_user_scope_refuses_automatic_strip_in_real_cli[True]"
]
},
{
"conformance_class": "consumer",
"id": "req-pr-001",
Expand Down Expand Up @@ -1515,7 +1555,7 @@
"xfail": 0
},
"governance": {
"active": 18,
"active": 20,
"skipped": 0,
"unbound": 0,
"xfail": 0
Expand All @@ -1533,5 +1573,5 @@
"xfail": 0
}
},
"total_requirements": 123
"total_requirements": 125
}
8 changes: 7 additions & 1 deletion CONFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ All four conformance classes (Producer, Consumer, Registry, Governance) carry ac

Repository-coordinate segments are case-insensitive for `github.com`, GitHub Enterprise Cloud hosts ending in `.ghe.com`, the literal GitHub Enterprise Server host selected by `GITHUB_HOST`, and registry-sourced dependencies (including registry prefixes). Local paths, marketplace identities, and every other host remain case-sensitive. Policy matching and repository identity use the same rule (req-rs-016 clause 3; req-pl-018).

## Optional deployed-prompt audit

APM claims the proposed deployed-prompt-audit capability (req-pl-019, req-pl-020), subject to the pending Section 9.3 amendment process, not adopted-spec approval. APM's own Consumer deployment implementation supplies the definitions: project and user scopes follow TargetProfile deployment roots and PrimitiveMapping formats, including shared native hook settings via native_hook_config. The target-by-target locations, supported formats, prompt applicability and exclusions are published in docs/src/content/docs/reference/cli/audit.md. ContentScanner checks hidden or suspicious Unicode code points that may conceal instructions, not semantic prompt-injection detection; a clean result is not a guarantee that prompt text is safe. App SQLite state and unrelated transcripts are excluded. Detection does not grant execution, ownership or remediation authority.

## Consumer user-scope disclosure

- Manifest: `~/.apm/apm.yml`
Expand All @@ -31,7 +35,7 @@ Repository-coordinate segments are case-insensitive for `github.com`, GitHub Ent
| Producer | 12 | 0 | 0 | 0 |
| Consumer | 91 | 1 | 0 | 0 |
| Registry | 1 | 0 | 0 | 0 |
| Governance | 18 | 0 | 0 | 0 |
| Governance | 20 | 0 | 0 | 0 |

## Per-requirement coverage

Expand Down Expand Up @@ -106,6 +110,8 @@ Repository-coordinate segments are case-insensitive for `github.com`, GitHub Ent
| [req-pl-016](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-016) | MUST | 6.8 | governance | active | 1 | - |
| [req-pl-017](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-017) | MUST | 6.8 | governance | active | 1 | - |
| [req-pl-018](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-018) | MUST | 6.3.1 | governance | active | 1 | - |
| [req-pl-019](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-019) | MUST | 6.8.1 | governance | active | 14 | - |
| [req-pl-020](docs/src/content/docs/specs/openapm-v0.1.md#req-pl-020) | MUST | 6.8.1 | governance | active | 3 | - |
| [req-pr-001](docs/src/content/docs/specs/openapm-v0.1.md#req-pr-001) | MUST | 8.2 | consumer | active | 1 | - |
| [req-pr-002](docs/src/content/docs/specs/openapm-v0.1.md#req-pr-002) | MUST | 8.3 | consumer | active | 2 | - |
| [req-pr-003](docs/src/content/docs/specs/openapm-v0.1.md#req-pr-003) | MUST | 8.3 | consumer | active | 1 | - |
Expand Down
8 changes: 8 additions & 0 deletions docs/public/specs/manifests/openapm-v0.1.requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,14 @@ requirements:
section: "6.3.1"
conformance_class: governance
notes: "allow, deny, and exact require package operands use the documented per-host repository case rule of req-rs-016 plus the registry-coordinate case rule; ASCII normalization does not cross virtual paths, refs, registry names, MCP names, unmanaged paths, or case-sensitive host/source identities; recursive-glob ambiguity remains case-sensitive and deny precedence remains unchanged"
- id: req-pl-019
keyword: MUST
section: "6.8.1"
conformance_class: governance
- id: req-pl-020
keyword: MUST
section: "6.8.1"
conformance_class: governance
- id: req-rs-001
keyword: MUST
section: "7.2"
Expand Down
6 changes: 6 additions & 0 deletions docs/src/content/docs/contributing/integration-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ also enforce one lockfile read per used root and integration phase, zero unused
reads, and at most 15x record visits when fixture size grows from N to 10N.
These counts do not claim a wall-clock speedup.

For fully native Claude fixtures, global compile verifies the installed revision
in `rules/revision.md`, absence of redundant `CLAUDE.md`, and unchanged complete
snapshots. Separate lifecycle cases retain unmatched fallback, policy refusal,
and protected explicit-cleanup coverage. Mixed Claude/Hermes fixtures additionally
check the Hermes revision and permit only its `AGENTS.md` compilation write.

Use a clean committed checkout. Set `BASE_SHA` to the full 40-hex commit SHA
of a locally available, distinct ancestor of the checked-out `HEAD`.
Set `REPORT_PATH` and `COMPLETION_PATH` to distinct, new paths outside the checkout:
Expand Down
19 changes: 18 additions & 1 deletion docs/src/content/docs/enterprise/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,13 @@ This prevents lockfile membership from shrinking silently. Shared merge-hook
targets and sidecars remain exempt because APM merges into user-owned files
rather than claiming them.

A whole-project scan checks **every regular file under the deploy trees your targets govern** for hidden Unicode, not only files recorded in `apm.lock.yaml`. Hash verification and positional `PACKAGE` scans remain lockfile-scoped because they need recorded ownership. Source content under `.apm/` is not added by the deploy-tree walk; install-time scanning owns that surface, while any `.apm/` path already recorded in the lockfile remains covered.
A whole-project scan discovers **recognized primitive filenames and patterns**
from resolved target profiles, unioned with recorded paths in `apm.lock.yaml`.
Both paths apply the same prompt/non-prompt distinction, including shared native
settings. Hash verification and positional `PACKAGE` scans remain
lockfile-scoped. Source content under `.apm/` is not added by discovery;
install-time scanning owns that surface, while recorded prompt documents remain
covered.

CI and remediation are separate commands because `--ci` and `--strip` are mutually exclusive:

Expand All @@ -233,6 +239,17 @@ deployment owners; see
[Baseline CI checks](../../reference/baseline-checks/#deployment-ledger-owners)
for the boundary and remediation.

Automatic audit separates discovery from prompt checks. It inventories
recognized hook definitions, including untracked entries in shared native
settings, but scans only documented prompt fields and prompt documents.
Commands, executables and unrelated settings are not prompt content; hooks
are never executed. Transcripts, history and caches are not walked.
Unreadable or unsupported recognized content exits nonzero with incomplete
coverage, not an unsafe-content verdict. File tracking does not establish
entry ownership or a hash baseline. Shared/structured settings and external
roots are not automatically rewritten by `--strip`; review them manually.
See [Discovery and prompt coverage](../../reference/cli/audit/#discovery-and-prompt-coverage).

:::tip[External scanners (Experimental)]
`apm audit` can also ingest findings from **third-party SARIF scanners** (Semgrep, CodeQL, NVIDIA SkillSpector, etc.) so a single audit run reports both APM's native findings and external tool results. See [External scanners](../../integrations/external-scanners/) for setup.
:::
Expand Down
8 changes: 4 additions & 4 deletions docs/src/content/docs/reference/baseline-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ the [policy schema](../policy-schema/).

### `content-integrity`

- **What it verifies.** Two signals across every deployed file (including local `.apm/` content via the synthesized self-entry), each with the scope its evidence allows:
1. Critical hidden Unicode (tag characters, bidi overrides, variation selectors 17-256, and similar steganographic markers), over **every file under the deploy trees the project's targets govern**. This signal needs no recorded baseline, so it is deliberately not limited to `deployed_files` -- otherwise a deployed file the lockfile omits would be exempt from scanning for as long as it stayed unrecorded.
- **What it verifies.** Two independent signals, including local `.apm/` deployments recorded through the synthesized self-entry:
1. Critical hidden Unicode in recognized prompt documents and decoded native prompt fields. Discovery uses each resolved target's filenames and primitive patterns, including untracked files and shared hook settings. Recorded paths use the same applicability rules: tracking a command or script does not turn it into prompt text. Hook definitions remain visible even when their command-only content is not applicable to Unicode prompt checks. Session transcripts, history, caches, command strings and referenced executables are not scanned. See [Discovery and prompt coverage](../cli/audit/#discovery-and-prompt-coverage) for the format boundaries.
2. SHA-256 drift between the on-disk content and the hash recorded in `deployed_file_hashes` at install time. Necessarily lockfile-scoped: an unrecorded file has no baseline to compare against.
- **Fails when.** Any deployed file contains a critical Unicode finding or its hash no longer matches the lockfile entry. Missing files are intentionally not reported here -- `deployed-files-present` owns that signal. Symlinks and entries without a recorded hash are skipped.
- **Remediation.** Run `apm audit --strip` to clean Unicode findings, and `apm install` to restore hash-drifted files. Both may be needed.
- **Fails when.** An applicable prompt contains critical Unicode, recognized content is unreadable or unsupported (incomplete coverage), a recorded hash differs, or a legacy hash lacks canonical ownership metadata. Missing files are reported by `deployed-files-present`; unrecorded files have no hash baseline. Existing path and symlink protections still apply.
- **Remediation.** Review the reported format or access error for incomplete coverage. Use `apm audit --strip` only for supported regular prompt documents; structured/shared settings and external-root findings require manual review. Run `apm install` to restore hash-drifted files. Discovery does not grant ownership or permission to rewrite user configuration.

### `includes-consent`

Expand Down
Loading
Loading