From 99457d710d0c8f14981bda864137df7b75feaccf Mon Sep 17 00:00:00 2001 From: Conal <33135619+Conalh@users.noreply.github.com> Date: Fri, 22 May 2026 09:29:40 -0700 Subject: [PATCH] Namespace finding kinds: prefix with 'policy_mesh.' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopts agent-gov-core v0.2.0 and prefixes every Finding.kind with the 'policy_mesh.' namespace per the suite-wide v1.0 wire-format wave (agent-gov-core#1). Kinds renamed (13): claude_broad_allow_no_guard, claude_deny_allow_overlap, claude_mcp_grant_missing_server, codex_claude_posture_gap, codex_network_without_review, codex_trusted_with_risky_mcp, config_parse_error, mcp_command_mismatch, mcp_enabled_mismatch, mcp_env_mismatch, mcp_header_mismatch, mcp_server_missing, mcp_unpinned Pure rename — no detector semantics changed, no severities changed. Test assertions updated. 39/39 tests passing. --- dist/mesh/engine.js | 24 +++++++++++----------- dist/parsers/errors.js | 2 +- package-lock.json | 2 +- package.json | 2 +- src/mesh/engine.ts | 24 +++++++++++----------- src/parsers/errors.ts | 2 +- test/cli-output.test.mjs | 44 ++++++++++++++++++++-------------------- 7 files changed, 50 insertions(+), 50 deletions(-) diff --git a/dist/mesh/engine.js b/dist/mesh/engine.js index 89ecc29..0df23e7 100644 --- a/dist/mesh/engine.js +++ b/dist/mesh/engine.js @@ -33,7 +33,7 @@ function detectClaudeMcpGrantMissingServer(policies) { continue; } findings.push({ - kind: 'claude_mcp_grant_missing_server', + kind: 'policy_mesh.claude_mcp_grant_missing_server', severity: 'medium', file: claude.file, line, @@ -69,7 +69,7 @@ function detectMcpCommandMismatch(policies) { .join(' vs '); const primary = servers[0]; findings.push({ - kind: 'mcp_command_mismatch', + kind: 'policy_mesh.mcp_command_mismatch', severity: 'high', file: primary.file, line: primary.line, @@ -102,7 +102,7 @@ function detectMcpServerMissing(policies) { } const primary = servers[0]; findings.push({ - kind: 'mcp_server_missing', + kind: 'policy_mesh.mcp_server_missing', severity: 'low', file: primary.file, line: primary.line, @@ -138,7 +138,7 @@ function detectMcpEnabledMismatch(policies) { } const primary = servers[0]; findings.push({ - kind: 'mcp_enabled_mismatch', + kind: 'policy_mesh.mcp_enabled_mismatch', severity: 'medium', file: primary.file, line: primary.line, @@ -171,7 +171,7 @@ function detectMcpEnvMismatch(policies) { const primary = servers[0]; const differingKeys = differingEnvKeys(servers); findings.push({ - kind: 'mcp_env_mismatch', + kind: 'policy_mesh.mcp_env_mismatch', severity: 'medium', file: primary.file, line: primary.line, @@ -206,7 +206,7 @@ function detectMcpHeaderMismatch(policies) { const primary = servers[0]; const differingKeys = differingHeaderKeys(servers); findings.push({ - kind: 'mcp_header_mismatch', + kind: 'policy_mesh.mcp_header_mismatch', severity: 'medium', file: primary.file, line: primary.line, @@ -233,7 +233,7 @@ function detectMcpUnpinned(policies) { continue; } findings.push({ - kind: 'mcp_unpinned', + kind: 'policy_mesh.mcp_unpinned', severity: 'medium', file: server.file, line: server.line, @@ -260,7 +260,7 @@ function detectClaudeDenyAllowOverlap(policies) { for (const deny of sensitiveDenies) { const line = claude.deny.get(deny); findings.push({ - kind: 'claude_deny_allow_overlap', + kind: 'policy_mesh.claude_deny_allow_overlap', severity: 'medium', file: claude.file, line, @@ -288,7 +288,7 @@ function detectClaudeBroadAllowNoGuard(policies) { } const [primaryAllow, line] = broadAllows[0]; findings.push({ - kind: 'claude_broad_allow_no_guard', + kind: 'policy_mesh.claude_broad_allow_no_guard', severity: 'medium', file: claude.file, line, @@ -309,7 +309,7 @@ function detectCodexNetworkWithoutReview(policies) { return findingsEmpty(); } return [{ - kind: 'codex_network_without_review', + kind: 'policy_mesh.codex_network_without_review', severity: 'medium', file: codex.file, line: codex.networkLine, @@ -330,7 +330,7 @@ function detectCodexTrustedWithRiskyMcp(policies) { if (unpinned.length > 0 || hasMismatch) { const risky = unpinned[0]; findings.push({ - kind: 'codex_trusted_with_risky_mcp', + kind: 'policy_mesh.codex_trusted_with_risky_mcp', severity: 'high', file: risky?.file ?? codex.file, line: risky?.line ?? codex.trustLine, @@ -364,7 +364,7 @@ function detectCodexClaudePostureGap(policies) { return findingsEmpty(); } return [{ - kind: 'codex_claude_posture_gap', + kind: 'policy_mesh.codex_claude_posture_gap', severity: 'medium', file: codex.file, line: codex.sandboxLine, diff --git a/dist/parsers/errors.js b/dist/parsers/errors.js index 6efc21a..7b18e6d 100644 --- a/dist/parsers/errors.js +++ b/dist/parsers/errors.js @@ -10,7 +10,7 @@ const SURFACE_NAMES = { export function configParseFinding(file, surface, parseError) { const syntax = surface === 'codex' ? 'TOML' : 'JSON'; return { - kind: 'config_parse_error', + kind: 'policy_mesh.config_parse_error', severity: 'high', file, line: parseError.line, diff --git a/package-lock.json b/package-lock.json index d51b0f6..fa87a49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.18", "license": "MIT", "dependencies": { - "agent-gov-core": "github:Conalh/agent-gov-core#v0.1.2" + "agent-gov-core": "github:Conalh/agent-gov-core#v0.2.0" }, "bin": { "policymesh": "dist/index.js" diff --git a/package.json b/package.json index 64ccb70..3f56736 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "test": "node --test" }, "dependencies": { - "agent-gov-core": "github:Conalh/agent-gov-core#v0.1.2" + "agent-gov-core": "github:Conalh/agent-gov-core#v0.2.0" }, "devDependencies": { "@types/node": "^24.0.0", diff --git a/src/mesh/engine.ts b/src/mesh/engine.ts index c2a5fc0..6285147 100644 --- a/src/mesh/engine.ts +++ b/src/mesh/engine.ts @@ -42,7 +42,7 @@ function detectClaudeMcpGrantMissingServer(policies: RepoPolicies): Finding[] { } findings.push({ - kind: 'claude_mcp_grant_missing_server', + kind: 'policy_mesh.claude_mcp_grant_missing_server', severity: 'medium', file: claude.file, line, @@ -83,7 +83,7 @@ function detectMcpCommandMismatch(policies: RepoPolicies): Finding[] { .join(' vs '); const primary = servers[0]; findings.push({ - kind: 'mcp_command_mismatch', + kind: 'policy_mesh.mcp_command_mismatch', severity: 'high', file: primary.file, line: primary.line, @@ -121,7 +121,7 @@ function detectMcpServerMissing(policies: RepoPolicies): Finding[] { const primary = servers[0]; findings.push({ - kind: 'mcp_server_missing', + kind: 'policy_mesh.mcp_server_missing', severity: 'low', file: primary.file, line: primary.line, @@ -162,7 +162,7 @@ function detectMcpEnabledMismatch(policies: RepoPolicies): Finding[] { const primary = servers[0]; findings.push({ - kind: 'mcp_enabled_mismatch', + kind: 'policy_mesh.mcp_enabled_mismatch', severity: 'medium', file: primary.file, line: primary.line, @@ -200,7 +200,7 @@ function detectMcpEnvMismatch(policies: RepoPolicies): Finding[] { const primary = servers[0]; const differingKeys = differingEnvKeys(servers); findings.push({ - kind: 'mcp_env_mismatch', + kind: 'policy_mesh.mcp_env_mismatch', severity: 'medium', file: primary.file, line: primary.line, @@ -240,7 +240,7 @@ function detectMcpHeaderMismatch(policies: RepoPolicies): Finding[] { const primary = servers[0]; const differingKeys = differingHeaderKeys(servers); findings.push({ - kind: 'mcp_header_mismatch', + kind: 'policy_mesh.mcp_header_mismatch', severity: 'medium', file: primary.file, line: primary.line, @@ -271,7 +271,7 @@ function detectMcpUnpinned(policies: RepoPolicies): Finding[] { } findings.push({ - kind: 'mcp_unpinned', + kind: 'policy_mesh.mcp_unpinned', severity: 'medium', file: server.file, line: server.line, @@ -303,7 +303,7 @@ function detectClaudeDenyAllowOverlap(policies: RepoPolicies): Finding[] { for (const deny of sensitiveDenies) { const line = claude.deny.get(deny); findings.push({ - kind: 'claude_deny_allow_overlap', + kind: 'policy_mesh.claude_deny_allow_overlap', severity: 'medium', file: claude.file, line, @@ -336,7 +336,7 @@ function detectClaudeBroadAllowNoGuard(policies: RepoPolicies): Finding[] { const [primaryAllow, line] = broadAllows[0]; findings.push({ - kind: 'claude_broad_allow_no_guard', + kind: 'policy_mesh.claude_broad_allow_no_guard', severity: 'medium', file: claude.file, line, @@ -361,7 +361,7 @@ function detectCodexNetworkWithoutReview(policies: RepoPolicies): Finding[] { } return [{ - kind: 'codex_network_without_review', + kind: 'policy_mesh.codex_network_without_review', severity: 'medium', file: codex.file, line: codex.networkLine, @@ -385,7 +385,7 @@ function detectCodexTrustedWithRiskyMcp(policies: RepoPolicies): Finding[] { if (unpinned.length > 0 || hasMismatch) { const risky = unpinned[0]; findings.push({ - kind: 'codex_trusted_with_risky_mcp', + kind: 'policy_mesh.codex_trusted_with_risky_mcp', severity: 'high', file: risky?.file ?? codex.file, line: risky?.line ?? codex.trustLine, @@ -424,7 +424,7 @@ function detectCodexClaudePostureGap(policies: RepoPolicies): Finding[] { } return [{ - kind: 'codex_claude_posture_gap', + kind: 'policy_mesh.codex_claude_posture_gap', severity: 'medium', file: codex.file, line: codex.sandboxLine, diff --git a/src/parsers/errors.ts b/src/parsers/errors.ts index 88c7669..4741fe8 100644 --- a/src/parsers/errors.ts +++ b/src/parsers/errors.ts @@ -15,7 +15,7 @@ export function configParseFinding(file: string, surface: SurfaceId, parseError: const syntax = surface === 'codex' ? 'TOML' : 'JSON'; return { - kind: 'config_parse_error', + kind: 'policy_mesh.config_parse_error', severity: 'high', file, line: parseError.line, diff --git a/test/cli-output.test.mjs b/test/cli-output.test.mjs index 4993ad9..aea5b25 100644 --- a/test/cli-output.test.mjs +++ b/test/cli-output.test.mjs @@ -123,16 +123,16 @@ test('CLI conflicted fixture returns high rating with expected kinds', async () assert.ok(report.findingCount >= 5); const kinds = report.findings.map((finding) => finding.kind); - assert.ok(kinds.includes('mcp_command_mismatch')); - assert.ok(kinds.includes('mcp_unpinned')); - assert.ok(kinds.includes('claude_deny_allow_overlap')); - assert.ok(kinds.includes('claude_broad_allow_no_guard')); - assert.ok(kinds.includes('codex_network_without_review')); - assert.ok(kinds.includes('codex_trusted_with_risky_mcp')); - assert.ok(kinds.includes('codex_claude_posture_gap')); + assert.ok(kinds.includes('policy_mesh.mcp_command_mismatch')); + assert.ok(kinds.includes('policy_mesh.mcp_unpinned')); + assert.ok(kinds.includes('policy_mesh.claude_deny_allow_overlap')); + assert.ok(kinds.includes('policy_mesh.claude_broad_allow_no_guard')); + assert.ok(kinds.includes('policy_mesh.codex_network_without_review')); + assert.ok(kinds.includes('policy_mesh.codex_trusted_with_risky_mcp')); + assert.ok(kinds.includes('policy_mesh.codex_claude_posture_gap')); const githubMismatch = report.findings.find( - (finding) => finding.kind === 'mcp_command_mismatch' && finding.subject === 'github' + (finding) => finding.kind === 'policy_mesh.mcp_command_mismatch' && finding.subject === 'github' ); assert.ok(githubMismatch); assert.ok(githubMismatch.surfaces.includes('codex')); @@ -153,7 +153,7 @@ test('CLI reports malformed agent config instead of crashing', async () => { assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 1); assert.ok(report.effectiveUnion.includes('1 unreadable agent config')); - assert.equal(report.findings[0].kind, 'config_parse_error'); + assert.equal(report.findings[0].kind, 'policy_mesh.config_parse_error'); assert.equal(report.findings[0].file, '.cursor/mcp.json'); assert.match(report.findings[0].message, /Could not parse Cursor MCP config/); }); @@ -172,7 +172,7 @@ test('CLI reports malformed Codex config instead of hiding the surface', async ( assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 1); assert.ok(report.effectiveUnion.includes('1 unreadable agent config')); - assert.equal(report.findings[0].kind, 'config_parse_error'); + assert.equal(report.findings[0].kind, 'policy_mesh.config_parse_error'); assert.equal(report.findings[0].file, '.codex/config.toml'); assert.equal(report.findings[0].line, 1); assert.match(report.findings[0].message, /Could not parse Codex config/); @@ -192,7 +192,7 @@ test('CLI reports Claude MCP grants whose server is not defined in MCP configs', assert.equal(report.rating, 'medium'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'claude_mcp_grant_missing_server'); + assert.equal(report.findings[0].kind, 'policy_mesh.claude_mcp_grant_missing_server'); assert.equal(report.findings[0].severity, 'medium'); assert.equal(report.findings[0].file, '.claude/settings.json'); assert.equal(report.findings[0].line, 4); @@ -215,7 +215,7 @@ test('CLI reports servers missing from configured but empty MCP surfaces', async assert.equal(report.rating, 'low'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_server_missing'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_server_missing'); assert.equal(report.findings[0].severity, 'low'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'cursor_mcp']); @@ -237,7 +237,7 @@ test('CLI reports MCP server enabled-state drift across surfaces', async () => { assert.equal(report.rating, 'medium'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_enabled_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_enabled_mismatch'); assert.equal(report.findings[0].severity, 'medium'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'cursor_mcp']); @@ -259,7 +259,7 @@ test('CLI reports MCP server environment drift without leaking values', async () assert.equal(report.rating, 'medium'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_env_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_env_mismatch'); assert.equal(report.findings[0].severity, 'medium'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'vscode_mcp']); @@ -284,7 +284,7 @@ test('CLI reports only differing MCP environment value keys without leaking valu assert.equal(report.rating, 'medium'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_env_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_env_mismatch'); assert.match(report.findings[0].message, /environment values differ/); assert.match(report.findings[0].message, /GITHUB_TOKEN/); assert.doesNotMatch(report.findings[0].message, /SHARED_TIMEOUT/); @@ -305,7 +305,7 @@ test('CLI reports MCP server header drift without leaking values', async () => { assert.equal(report.rating, 'medium'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_header_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_header_mismatch'); assert.equal(report.findings[0].severity, 'medium'); assert.equal(report.findings[0].subject, 'analytics'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'vscode_mcp']); @@ -330,7 +330,7 @@ test('CLI reports Codex MCP server command drift against root MCP config', async assert.equal(report.rating, 'high'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_command_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_command_mismatch'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'codex']); assert.ok(report.findings[0].locations.some((location) => location.file === '.codex/config.toml' && location.surface === 'codex')); @@ -355,7 +355,7 @@ test('CLI does not flag mcp_command_mismatch on neutral -y flag drift between su const report = JSON.parse(stdout); const mismatchFindings = report.findings.filter( - (finding) => finding.kind === 'mcp_command_mismatch' + (finding) => finding.kind === 'policy_mesh.mcp_command_mismatch' ); assert.deepEqual(mismatchFindings, [], 'expected no mcp_command_mismatch findings'); assert.equal(report.surfaceCount, 2); @@ -374,7 +374,7 @@ test('CLI reports Codeium plugin MCP command drift against root MCP config', asy assert.equal(report.rating, 'high'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_command_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_command_mismatch'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'codeium_mcp']); assert.ok(report.findings[0].locations.some((location) => location.file === '.codeium/mcp_config.json' && location.surface === 'codeium_mcp')); @@ -394,7 +394,7 @@ test('CLI reports only differing MCP header value keys without leaking values', assert.equal(report.rating, 'medium'); assert.equal(report.findingCount, 1); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'mcp_header_mismatch'); + assert.equal(report.findings[0].kind, 'policy_mesh.mcp_header_mismatch'); assert.match(report.findings[0].message, /header values differ/); assert.match(report.findings[0].message, /Authorization/); assert.doesNotMatch(report.findings[0].message, /X-Org/); @@ -415,9 +415,9 @@ test('CLI reports Codex network access alongside unreadable agent surfaces', asy assert.equal(report.rating, 'high'); assert.equal(report.findingCount, 2); assert.equal(report.surfaceCount, 2); - assert.equal(report.findings[0].kind, 'config_parse_error'); + assert.equal(report.findings[0].kind, 'policy_mesh.config_parse_error'); assert.equal(report.findings[0].file, '.cursor/mcp.json'); - assert.equal(report.findings[1].kind, 'codex_network_without_review'); + assert.equal(report.findings[1].kind, 'policy_mesh.codex_network_without_review'); assert.equal(report.findings[1].severity, 'medium'); assert.equal(report.findings[1].file, '.codex/config.toml'); assert.equal(report.findings[1].line, 1);