@event4u/agent-config 9.6.0 → 9.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +207 -942
- package/README.md +21 -21
- package/dist/agent-src/README.md +2 -2
- package/dist/agent-src/commands/commit/in-chunks.md +8 -0
- package/dist/agent-src/commands/commit.md +17 -0
- package/dist/agent-src/commands/pr/create.md +4 -0
- package/dist/agent-src/contexts/override-system.md +75 -5
- package/dist/agent-src/rules/context-hygiene.md +2 -0
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +97 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/downstream-changes.md +31 -1
- package/dist/agent-src/rules/external-code-graph-interop.md +13 -3
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +2 -0
- package/dist/agent-src/rules/git-history-discipline.md +2 -0
- package/dist/agent-src/rules/language-and-tone.md +2 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
- package/dist/agent-src/rules/media-governance-routing.md +2 -0
- package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
- package/dist/agent-src/rules/no-roadmap-references.md +3 -0
- package/dist/agent-src/rules/non-destructive-by-default.md +20 -0
- package/dist/agent-src/rules/onboarding-gate.md +2 -0
- package/dist/agent-src/rules/output-discipline.md +2 -0
- package/dist/agent-src/rules/persona-governance.md +2 -0
- package/dist/agent-src/rules/preservation-guard.md +3 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +106 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +1 -1
- package/dist/agent-src/rules/skill-quality.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +2 -0
- package/dist/agent-src/rules/source-of-truth.md +2 -0
- package/dist/agent-src/rules/telegraph-speak.md +2 -0
- package/dist/agent-src/rules/token-optimizer-maintenance.md +2 -0
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/verify-before-complete.md +2 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +17 -1
- package/dist/agent-src/skills/agent-docs-writing/SKILL.md +34 -16
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +9 -9
- package/dist/agent-src/skills/code-intelligence/SKILL.md +94 -0
- package/dist/agent-src/skills/gated-reach/SKILL.md +185 -0
- package/dist/agent-src/skills/gated-reach/evals/triggers.json +72 -0
- package/dist/agent-src/skills/image-analyser/SKILL.md +14 -12
- package/dist/agent-src/skills/image-editing/SKILL.md +1 -0
- package/dist/agent-src/skills/readme-writing/SKILL.md +6 -1
- package/dist/agent-src/skills/screenshot-hygiene/SKILL.md +171 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +44 -1
- package/dist/agent-src/skills/subagent-orchestration/prompts/adversarial-verification-council.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-and-judge-two-stage.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-and-judge.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-competitively.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-parallel.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-steps.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-worktrees.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/judge-with-debate.md +29 -0
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +4 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/github-workflows/doc-impact.yml +101 -0
- package/dist/agent-src/templates/github-workflows/secret-scan.yml +65 -0
- package/dist/cli/configRoot.js +112 -0
- package/dist/cli/configRoot.js.map +1 -0
- package/dist/cli/configRoot.test.js +97 -0
- package/dist/cli/configRoot.test.js.map +1 -0
- package/dist/cli/help.js +4 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +19 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +164 -47
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +13 -8
- package/dist/discovery/trust-report.md +11 -11
- package/dist/discovery/workspaces.json +11 -6
- package/dist/install/install.mjs +16 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
- package/dist/mcp/registry-manifest.json +4 -4
- package/dist/router.json +1 -1
- package/dist/server/app.js +10 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/routes/ping.js +18 -0
- package/dist/server/routes/ping.js.map +1 -1
- package/dist/server/schemas/settings.js +8 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/server/serverInfo.js +6 -2
- package/dist/server/serverInfo.js.map +1 -1
- package/dist/server/token.js +11 -4
- package/dist/server/token.js.map +1 -1
- package/dist/server/writeRoot.js +15 -1
- package/dist/server/writeRoot.js.map +1 -1
- package/dist/shared/capabilities.js +33 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/capabilities.test.js +39 -0
- package/dist/shared/capabilities.test.js.map +1 -0
- package/dist/ui/assets/{index-BiEZiZ31.js → index-CrmZ34Rg.js} +16 -16
- package/dist/ui/assets/index-CrmZ34Rg.js.map +1 -0
- package/dist/ui/index.html +14 -5
- package/docs/CLAIMS.md +64 -38
- package/docs/architecture.md +2 -2
- package/docs/archive/CHANGELOG-6.0.0-overview.md +953 -0
- package/docs/benchmark.md +170 -9
- package/docs/catalog.md +8 -3
- package/docs/comparison.yaml +44 -2
- package/docs/contracts/local-server-ports.md +53 -0
- package/docs/contracts/no-runtime-boundary.md +1 -1
- package/docs/contracts/subagent-boundary.md +15 -0
- package/docs/customization.md +14 -3
- package/docs/decisions/ADR-016-installer-architecture.md +2 -0
- package/docs/decisions/ADR-088-no-external-runtime-federation.md +1 -1
- package/docs/decisions/ADR-094-agent-memory-layer-removal.md +1 -1
- package/docs/decisions/ADR-109-subagent-v1-contract.md +2 -0
- package/docs/decisions/ADR-110-discipline-profile-resolution-locus.md +2 -0
- package/docs/decisions/ADR-113-ci-native-release-label-trigger.md +11 -3
- package/docs/decisions/ADR-122-adversarial-verification-council.md +30 -0
- package/docs/decisions/ADR-123-runtime-security-scope-and-spawn-hardening.md +31 -0
- package/docs/decisions/ADR-124-embedded-engine-doctrine.md +255 -0
- package/docs/decisions/ADR-125-doc-screenshot-anonymization.md +131 -0
- package/docs/decisions/ADR-126-internet-reach-operator-tooling.md +254 -0
- package/docs/decisions/ADR-127-enforcement-claims-must-resolve.md +150 -0
- package/docs/decisions/ADR-128-executable-evidence-and-measured-escalation.md +201 -0
- package/docs/decisions/INDEX.md +21 -0
- package/docs/decisions/engine-reclassification-2026-07.md +104 -0
- package/docs/distribution/awesome-list-submission.md +1 -1
- package/docs/distribution/registries.md +36 -27
- package/docs/enforcement-by-host.md +7 -0
- package/docs/featured-skills.md +2 -2
- package/docs/getting-started-by-role.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/governance-advantage.md +1 -1
- package/docs/guides/gated-platform-reads.md +221 -0
- package/docs/positioning-evidence.md +1 -1
- package/docs/proof.md +92 -16
- package/docs/spawn-site-policy.md +72 -0
- package/docs/threat-model.md +2 -1
- package/package.json +6 -3
- package/src/agent-src/README.md +2 -2
- package/src/config/agent-settings.template.yml +43 -0
- package/src/config/reach-channels.yml +217 -0
- package/src/config/reach-prescriptions-intake.yml +200 -0
- package/src/scripts/_dispatch.bash +15 -0
- package/src/scripts/_lib/adversarial_bench_score.ts +106 -0
- package/src/scripts/_lib/exec_evidence.ts +251 -0
- package/src/scripts/_lib/fixtures/secret-detector/negatives.txt +10 -0
- package/src/scripts/_lib/fixtures/secret-detector/positives.txt +11 -0
- package/src/scripts/_lib/kernel_rules.ts +46 -0
- package/src/scripts/_lib/reddit_thread_parse.ts +598 -0
- package/src/scripts/_lib/risk_paths.ts +160 -0
- package/src/scripts/_lib/secret_detector.test.ts +103 -0
- package/src/scripts/_lib/secret_detector.ts +319 -0
- package/src/scripts/_lib/spawn_env.ts +24 -4
- package/src/scripts/_lib/tool_probe.ts +359 -0
- package/src/scripts/adr/regenerate_index.ts +81 -6
- package/src/scripts/audit_adr_coverage.ts +33 -0
- package/src/scripts/bench_adversarial_council.ts +292 -0
- package/src/scripts/build_proof.ts +162 -0
- package/src/scripts/check_adr_frontmatter.ts +195 -0
- package/src/scripts/check_artefact_count_messaging.ts +4 -0
- package/src/scripts/check_backstop_debt.ts +200 -0
- package/src/scripts/check_claims.ts +188 -8
- package/src/scripts/check_comparison.ts +12 -0
- package/src/scripts/check_enforcement_coverage.ts +592 -0
- package/src/scripts/check_kernel_rule_bundle.ts +16 -12
- package/src/scripts/check_no_new_legacy_path.ts +2 -0
- package/src/scripts/check_reach_channels.ts +407 -0
- package/src/scripts/check_reach_staleness.ts +364 -0
- package/src/scripts/check_secret_leak.test.ts +47 -0
- package/src/scripts/check_secret_leak.ts +290 -0
- package/src/scripts/check_source_pointer_freshness.ts +165 -0
- package/src/scripts/code_graph/build.ts +0 -0
- package/src/scripts/code_graph/cli.ts +207 -0
- package/src/scripts/code_graph/detect.ts +134 -0
- package/src/scripts/code_graph/extract.ts +386 -0
- package/src/scripts/code_graph/loader.ts +123 -0
- package/src/scripts/code_graph/query.ts +176 -0
- package/src/scripts/code_graph/sanitize.ts +30 -0
- package/src/scripts/code_graph/types.ts +83 -0
- package/src/scripts/code_graph/validate.ts +63 -0
- package/src/scripts/external_sources_denylist.json +3 -1
- package/src/scripts/generate_subagent_floor.ts +194 -0
- package/src/scripts/hook_manifest.yaml +13 -3
- package/src/scripts/hooks/code_graph_nudge_hook.ts +184 -0
- package/src/scripts/hooks/dispatch_hook.ts +5 -1
- package/src/scripts/iron_law_sha.ts +3 -12
- package/src/scripts/lint_override_kernel_guard.ts +219 -0
- package/src/scripts/lint_readme_serial_comma.ts +169 -0
- package/src/scripts/measure_rule_budget.ts +3 -12
- package/src/scripts/postinstall_gui.ts +129 -0
- package/src/scripts/reach_doctor.ts +1651 -0
- package/src/scripts/release.ts +24 -3
- package/src/scripts/risk_escalation_shadow.ts +160 -0
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/reach-channels.schema.json +134 -0
- package/src/scripts/schemas/reach-doctor-payload.schema.json +220 -0
- package/src/scripts/schemas/rule.schema.json +10 -0
- package/src/scripts/skill_linter.ts +55 -0
- package/src/scripts/validate_reach_prescriptions.ts +751 -0
- package/src/server/schemas/settings.ts +16 -0
- package/src/shared/capabilities.test.ts +43 -0
- package/src/shared/capabilities.ts +75 -0
- package/src/templates/minimal/agents-overrides-readme.md +20 -0
- package/dist/ui/assets/index-BiEZiZ31.js.map +0 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `exec:` evidence — the ledger form that re-derives its own claim.
|
|
3
|
+
*
|
|
4
|
+
* The other three evidence forms are existence checks: a file is present, a
|
|
5
|
+
* substring is present, a URL carries a date. None of them can tell a live
|
|
6
|
+
* claim from a stale one. A ledger entry reading "the suite is green" whose
|
|
7
|
+
* pointer resolves to a report nobody regenerated stays `backed` forever.
|
|
8
|
+
*
|
|
9
|
+
* `exec:<command> -> 0` closes that: the command runs again and its exit code
|
|
10
|
+
* carries the verdict. Three properties make it safe enough to run in CI, and
|
|
11
|
+
* all three are the parts that get dropped when this is rebuilt from memory:
|
|
12
|
+
*
|
|
13
|
+
* 1. The allowlist is a set of **argv prefix tuples**, never a regex over a
|
|
14
|
+
* command string. A regex over shell text is the classic bypass; a tuple
|
|
15
|
+
* compared against parsed argv has nothing to hide behind.
|
|
16
|
+
* 2. Every argument *after* the matched prefix is re-checked — for shell
|
|
17
|
+
* metacharacters and for repo escape — including the right-hand side of
|
|
18
|
+
* `--flag=value`. Without that split, `--rootdir=/etc` clears an allowlist
|
|
19
|
+
* that only ever looked at the flag name.
|
|
20
|
+
* 3. Execution is `spawnSync` with an argv array and no shell, confined to the
|
|
21
|
+
* repo root, and gated to CI. Locally the caller reports UNVERIFIED rather
|
|
22
|
+
* than running anything — see `exec_allowed_here()`.
|
|
23
|
+
*
|
|
24
|
+
* Scope limit, stated rather than discovered later: this form only covers
|
|
25
|
+
* claims whose exit code *is* the verdict. A claim resting on a paid model run,
|
|
26
|
+
* a stochastic benchmark, or a prose contract cannot use it, and is recorded as
|
|
27
|
+
* unfalsifiable-by-machine instead of quietly omitted.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { spawnSync } from 'node:child_process';
|
|
31
|
+
import path from 'node:path';
|
|
32
|
+
|
|
33
|
+
/** Marker that opens the fourth evidence form. */
|
|
34
|
+
export const EXEC_PREFIX = 'exec:';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Allowlisted argv prefixes. Each entry is matched element-by-element against
|
|
38
|
+
* the parsed argv — `["skill_eval_coverage"]` admits
|
|
39
|
+
* `skill_eval_coverage --check` but not `skill_eval_coverage; rm -rf /`,
|
|
40
|
+
* because the latter never parses into that shape.
|
|
41
|
+
*
|
|
42
|
+
* All but one are this repo's own scripts, run through `scripts-run`. That is a
|
|
43
|
+
* deliberately narrower surface than a general test-runner allowlist: these are
|
|
44
|
+
* deterministic, model-free, and already run in CI for other reasons.
|
|
45
|
+
*/
|
|
46
|
+
export const ALLOWLIST_PREFIXES: readonly (readonly string[])[] = [
|
|
47
|
+
['skill_eval_coverage'],
|
|
48
|
+
['domain_soundness_status'],
|
|
49
|
+
['check_artefact_count_messaging'],
|
|
50
|
+
['check_enforcement_coverage'],
|
|
51
|
+
['check_token_regression'],
|
|
52
|
+
['lint_agent_security'],
|
|
53
|
+
['measure_lexical_ranking'],
|
|
54
|
+
['vitest', 'run'],
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/** Shell metacharacters plus the control bytes used to smuggle a second command. */
|
|
58
|
+
const UNSAFE_CHARS = new Set(['&', ';', '|', '>', '<', '`', '$', '\n', '\r', '\0']);
|
|
59
|
+
|
|
60
|
+
const WINDOWS_ABSOLUTE = /^[A-Za-z]:[\\/]/;
|
|
61
|
+
|
|
62
|
+
/** Commands that are not repo scripts and must not be routed through scripts-run. */
|
|
63
|
+
const NON_SCRIPT_PREFIXES = new Set(['vitest']);
|
|
64
|
+
|
|
65
|
+
export interface ExecPointer {
|
|
66
|
+
/** The command as written in the ledger, e.g. `skill_eval_coverage --check`. */
|
|
67
|
+
command: string;
|
|
68
|
+
/** The exit code the claim asserts. */
|
|
69
|
+
expected: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ExecOutcome {
|
|
73
|
+
/** `true` only when the command ran AND matched the asserted exit code. */
|
|
74
|
+
verified: boolean;
|
|
75
|
+
/** `true` when the command ran and disagreed — a real finding, not a skip. */
|
|
76
|
+
mismatch: boolean;
|
|
77
|
+
/** Human-readable verdict for the report. */
|
|
78
|
+
reason: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse `exec:<command> -> <code>`. Accepts `->` and the `→` the docs render.
|
|
83
|
+
* Returns null when `raw` is not an exec pointer at all, so callers can fall
|
|
84
|
+
* through to the path/URL forms.
|
|
85
|
+
*/
|
|
86
|
+
export function parse_exec_pointer(raw: string): ExecPointer | { error: string } | null {
|
|
87
|
+
const ev = raw.trim();
|
|
88
|
+
if (!ev.startsWith(EXEC_PREFIX)) return null;
|
|
89
|
+
|
|
90
|
+
const body = ev.slice(EXEC_PREFIX.length).trim();
|
|
91
|
+
const arrow = body.match(/\s*(?:->|→)\s*(\d+)\s*$/);
|
|
92
|
+
if (!arrow) return { error: 'exec pointer missing the `-> <exit-code>` suffix' };
|
|
93
|
+
|
|
94
|
+
const command = body.slice(0, arrow.index).trim();
|
|
95
|
+
if (!command) return { error: 'exec pointer has an empty command' };
|
|
96
|
+
|
|
97
|
+
return { command, expected: Number(arrow[1]) };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Split a command into argv without invoking a shell. Deliberately minimal: no
|
|
102
|
+
* quote handling, no expansion, no substitution. Anything that would *need*
|
|
103
|
+
* those is rejected by `args_are_safe` anyway, so supporting them would only
|
|
104
|
+
* widen the surface.
|
|
105
|
+
*/
|
|
106
|
+
export function split_argv(command: string): string[] {
|
|
107
|
+
return command.split(/\s+/).filter((t) => t.length > 0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Does argv start with an allowlisted prefix? */
|
|
111
|
+
export function command_is_allowlisted(argv: string[]): boolean {
|
|
112
|
+
return ALLOWLIST_PREFIXES.some(
|
|
113
|
+
(prefix) => argv.length >= prefix.length && prefix.every((tok, i) => argv[i] === tok),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Would this argument reach outside the repo? */
|
|
118
|
+
function targets_outside_repo(candidate: string): boolean {
|
|
119
|
+
if (candidate.startsWith('/')) return true;
|
|
120
|
+
if (WINDOWS_ABSOLUTE.test(candidate)) return true;
|
|
121
|
+
if (candidate === '..' || candidate.startsWith('../')) return true;
|
|
122
|
+
return candidate.includes('/../');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Re-check every argument after the allowlisted prefix.
|
|
127
|
+
*
|
|
128
|
+
* The `--flag=value` split is the part worth keeping: an allowlist that only
|
|
129
|
+
* inspects the token as written treats `--rootdir=/etc` as a flag and lets the
|
|
130
|
+
* path through. Here the right-hand side is extracted and checked on its own.
|
|
131
|
+
*/
|
|
132
|
+
export function args_are_safe(argv: string[]): boolean {
|
|
133
|
+
for (const arg of argv.slice(1)) {
|
|
134
|
+
for (const ch of arg) {
|
|
135
|
+
if (UNSAFE_CHARS.has(ch)) return false;
|
|
136
|
+
}
|
|
137
|
+
const eq = arg.indexOf('=');
|
|
138
|
+
const candidate = arg.startsWith('-') && eq !== -1 ? arg.slice(eq + 1) : arg;
|
|
139
|
+
if (candidate && targets_outside_repo(candidate)) return false;
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Static validation — everything checkable without running anything.
|
|
146
|
+
*
|
|
147
|
+
* Split out from `run_exec_evidence` on purpose: a malformed or non-allowlisted
|
|
148
|
+
* command is a defect in the *ledger*, so it must fail on a laptop too. Only
|
|
149
|
+
* the re-execution half is environment-dependent. Returns null when the pointer
|
|
150
|
+
* is well-formed and admissible, else the reason.
|
|
151
|
+
*/
|
|
152
|
+
export function exec_static_error(pointer: ExecPointer): string | null {
|
|
153
|
+
const argv = split_argv(pointer.command);
|
|
154
|
+
if (argv.length === 0) return 'exec pointer has an empty command';
|
|
155
|
+
if (!command_is_allowlisted(argv)) {
|
|
156
|
+
return `exec command \`${pointer.command}\` is not in the deterministic allowlist`;
|
|
157
|
+
}
|
|
158
|
+
if (!args_are_safe(argv)) {
|
|
159
|
+
return `exec command \`${pointer.command}\` carries a suspicious argument`;
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* May this environment execute evidence commands?
|
|
166
|
+
*
|
|
167
|
+
* CI only. A consumer's checkout never re-runs a claim's command — the upstream
|
|
168
|
+
* package's ledger is not the consumer's business, and "runs in the repository
|
|
169
|
+
* root with full filesystem access" is a property to confine, not to trust.
|
|
170
|
+
*/
|
|
171
|
+
export function exec_allowed_here(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
172
|
+
if (env.AGENT_CONFIG_EXEC_EVIDENCE === '1') return true; // maintainer opt-in, one run
|
|
173
|
+
return env.CI === 'true' || env.CI === '1';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Build the real argv: repo scripts go through `scripts-run`, others run directly. */
|
|
177
|
+
function resolve_runner(argv: string[], repo: string): { file: string; args: string[] } {
|
|
178
|
+
const head = argv[0] as string;
|
|
179
|
+
if (NON_SCRIPT_PREFIXES.has(head)) {
|
|
180
|
+
return { file: path.join(repo, 'node_modules', '.bin', head), args: argv.slice(1) };
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
file: path.join(repo, 'scripts-run'),
|
|
184
|
+
args: [`src/scripts/${head}`, ...argv.slice(1)],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Run an allowlisted evidence command and compare its exit code to the claim.
|
|
190
|
+
*
|
|
191
|
+
* Never throws: every failure path returns an `ExecOutcome` so one bad pointer
|
|
192
|
+
* cannot take down the whole ledger check. `mismatch` distinguishes "ran and
|
|
193
|
+
* disagreed" (a finding) from "could not run" (a skip) — collapsing those two
|
|
194
|
+
* is how a verifier turns into a rubber stamp.
|
|
195
|
+
*/
|
|
196
|
+
export function run_exec_evidence(pointer: ExecPointer, repo: string, timeoutMs = 300_000): ExecOutcome {
|
|
197
|
+
const argv = split_argv(pointer.command);
|
|
198
|
+
|
|
199
|
+
if (argv.length === 0) {
|
|
200
|
+
return { verified: false, mismatch: false, reason: 'UNVERIFIED: empty command' };
|
|
201
|
+
}
|
|
202
|
+
if (!command_is_allowlisted(argv)) {
|
|
203
|
+
return {
|
|
204
|
+
verified: false,
|
|
205
|
+
mismatch: false,
|
|
206
|
+
reason: `UNVERIFIED: \`${pointer.command}\` is not in the deterministic allowlist`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (!args_are_safe(argv)) {
|
|
210
|
+
return {
|
|
211
|
+
verified: false,
|
|
212
|
+
mismatch: false,
|
|
213
|
+
reason: `UNVERIFIED: \`${pointer.command}\` carries a suspicious argument`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const { file, args } = resolve_runner(argv, repo);
|
|
218
|
+
const result = spawnSync(file, args, {
|
|
219
|
+
cwd: repo,
|
|
220
|
+
encoding: 'utf-8',
|
|
221
|
+
timeout: timeoutMs,
|
|
222
|
+
shell: false,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (result.error) {
|
|
226
|
+
return {
|
|
227
|
+
verified: false,
|
|
228
|
+
mismatch: false,
|
|
229
|
+
reason: `UNVERIFIED: could not execute \`${pointer.command}\` (${result.error.message})`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (result.status === null) {
|
|
233
|
+
return {
|
|
234
|
+
verified: false,
|
|
235
|
+
mismatch: false,
|
|
236
|
+
reason: `UNVERIFIED: \`${pointer.command}\` was killed (timeout or signal)`,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (result.status !== pointer.expected) {
|
|
240
|
+
return {
|
|
241
|
+
verified: false,
|
|
242
|
+
mismatch: true,
|
|
243
|
+
reason: `evidence mismatch — claim asserts exit ${pointer.expected}, \`${pointer.command}\` exited ${result.status}`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
verified: true,
|
|
248
|
+
mismatch: false,
|
|
249
|
+
reason: `verified \`${pointer.command}\` (exit ${result.status})`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# secret-detector negative fixtures — all benign, must not flag high/medium.
|
|
2
|
+
api_key = "changeme"
|
|
3
|
+
api_key = "xxxxxxxxxxxxxxxx"
|
|
4
|
+
password = "your-password-here"
|
|
5
|
+
build commit d41d8cd98f00b204e9800998ecf8427e5f1a2b3c
|
|
6
|
+
request id 550e8400-e29b-41d4-a716-446655440000
|
|
7
|
+
bundle integrity 3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b
|
|
8
|
+
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAElFTkSuQmCC
|
|
9
|
+
API_KEY=your-key-here
|
|
10
|
+
aws_key = "AKIAZ4Q7WXYT2H9RVBKL" # secret-allow
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# secret-detector positive fixtures — every value below is FAKE, do not use.
|
|
2
|
+
# Line format: <kind-label>|<fake-secret>
|
|
3
|
+
aws|AKIA1234567890ABCDEF
|
|
4
|
+
github|ghp_0123456789abcdefghijklmnopqrstuvwxyz
|
|
5
|
+
stripe|sk_live_0123456789abcdefABCDEF
|
|
6
|
+
google|AIza0123456789abcdefghijklmnopqrstuvwxy
|
|
7
|
+
slack|xoxb-0123456789-abcdefABCDEF
|
|
8
|
+
pem|-----BEGIN RSA PRIVATE KEY-----
|
|
9
|
+
jwt|eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
|
10
|
+
dburl|postgres://dbuser:s3cr3tpass@db.internal:5432/app
|
|
11
|
+
assignment|api_key = "s3cr3tV4lu3Abc123"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The locked kernel set — one definition, imported everywhere.
|
|
3
|
+
*
|
|
4
|
+
* These nine rules are the Iron-Law floor (`docs/contracts/kernel-membership.md`
|
|
5
|
+
* § 4). Before this module the list existed three times — in
|
|
6
|
+
* `check_kernel_rule_bundle.ts`, `iron_law_sha.ts`, and `measure_rule_budget.ts`
|
|
7
|
+
* — two of them carrying a "kept in sync with …" comment, which is the standard
|
|
8
|
+
* way a list drifts: the comment records an intention, nothing enforces it.
|
|
9
|
+
*
|
|
10
|
+
* Membership is a governance decision, not a convenience. Adding or removing an
|
|
11
|
+
* entry changes what may never be overridden, what the prefix-stability snapshot
|
|
12
|
+
* covers, and what the one-kernel-rule-per-PR gate applies to. Change it here,
|
|
13
|
+
* once, and let every consumer follow.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Kernel rule ids, stem form (no extension), in stable sorted order. */
|
|
17
|
+
export const KERNEL_RULE_IDS: readonly string[] = [
|
|
18
|
+
'agent-authority',
|
|
19
|
+
'ask-when-uncertain',
|
|
20
|
+
'commit-policy',
|
|
21
|
+
'direct-answers',
|
|
22
|
+
'language-and-tone',
|
|
23
|
+
'no-cheap-questions',
|
|
24
|
+
'non-destructive-by-default',
|
|
25
|
+
'scope-control',
|
|
26
|
+
'verify-before-complete',
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
/** Same set, `.md` filename form — what path-matching consumers compare against. */
|
|
30
|
+
export const KERNEL_RULE_FILENAMES: ReadonlySet<string> = new Set(
|
|
31
|
+
KERNEL_RULE_IDS.map((id) => `${id}.md`),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
/** Set form of the stems, for membership tests that key on the id. */
|
|
35
|
+
export const KERNEL_RULE_ID_SET: ReadonlySet<string> = new Set(KERNEL_RULE_IDS);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* True when `name` names a kernel rule, in either stem or `.md` form.
|
|
39
|
+
*
|
|
40
|
+
* Accepts a bare id, a filename, or a path ending in either — callers hold the
|
|
41
|
+
* name in all three shapes and should not each re-derive the normalisation.
|
|
42
|
+
*/
|
|
43
|
+
export function is_kernel_rule(name: string): boolean {
|
|
44
|
+
const base = name.replace(/\\/g, '/').split('/').pop() ?? name;
|
|
45
|
+
return KERNEL_RULE_ID_SET.has(base.replace(/\.md$/, ''));
|
|
46
|
+
}
|