altimate-receipts 0.18.0 → 0.19.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/dist/{chunk-IDAIDLZT.js → chunk-44V3DNK5.js} +4 -2
- package/dist/chunk-44V3DNK5.js.map +1 -0
- package/dist/{chunk-FPDXOAYN.js → chunk-C7NAN2LL.js} +61 -17
- package/dist/chunk-C7NAN2LL.js.map +1 -0
- package/dist/{chunk-UTOGDDME.js → chunk-COMWJSBT.js} +2 -2
- package/dist/{chunk-DNKSESAA.js → chunk-MJ3ZJNPX.js} +224 -8
- package/dist/chunk-MJ3ZJNPX.js.map +1 -0
- package/dist/{chunk-YO6MVZTD.js → chunk-VKT2ZUCT.js} +4 -3
- package/dist/chunk-VKT2ZUCT.js.map +1 -0
- package/dist/cli.js +68 -92
- package/dist/cli.js.map +1 -1
- package/dist/gen-catalog.js +2 -2
- package/dist/index.js +10 -4
- package/dist/mcp/server.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-DNKSESAA.js.map +0 -1
- package/dist/chunk-FPDXOAYN.js.map +0 -1
- package/dist/chunk-IDAIDLZT.js.map +0 -1
- package/dist/chunk-YO6MVZTD.js.map +0 -1
- /package/dist/{chunk-UTOGDDME.js.map → chunk-COMWJSBT.js.map} +0 -0
|
@@ -27,6 +27,7 @@ var PRIVILEGED_PREFIXES = [
|
|
|
27
27
|
"sensitive-access",
|
|
28
28
|
"guard-blocked",
|
|
29
29
|
"mcp-write",
|
|
30
|
+
"mcp-config-change",
|
|
30
31
|
"metadata-egress",
|
|
31
32
|
"tls-verify-disabled",
|
|
32
33
|
"setuid-create",
|
|
@@ -43,7 +44,8 @@ function privileged(id, filePath) {
|
|
|
43
44
|
return id.startsWith("file-shrink") && !!filePath && TEST_PATH.test(filePath);
|
|
44
45
|
}
|
|
45
46
|
function findingSurface(id) {
|
|
46
|
-
|
|
47
|
+
const base = id.replace(/-sa-[0-9a-f]+$/i, "");
|
|
48
|
+
if (OPERATOR_KINDS.has(base) || base.startsWith("errcluster-")) {
|
|
47
49
|
return "operator";
|
|
48
50
|
}
|
|
49
51
|
return "merge";
|
|
@@ -102,4 +104,4 @@ export {
|
|
|
102
104
|
regenerableReceiptRm,
|
|
103
105
|
regenerableBuildArtifact
|
|
104
106
|
};
|
|
105
|
-
//# sourceMappingURL=chunk-
|
|
107
|
+
//# sourceMappingURL=chunk-44V3DNK5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/findings/surface.ts"],"sourcesContent":["/**\n * Which surface a finding belongs to. The **merge gate** (the PR comment) is about\n * the *merged artifact* — what a reviewer must check before merging. The **operator**\n * view (the `receipts` card and `receipts trends`) is about *how the agent worked* —\n * efficiency, cost, loops, retries. One source of truth, shared by the PR-comment\n * renderer and its standalone Action mirror.\n */\n\n/** Kinds that describe agent behaviour/efficiency, not the merged change. */\nconst OPERATOR_KINDS = new Set([\n \"loop\",\n \"bottleneck\",\n \"cost-concentration\",\n \"cache-opportunity\",\n \"model-downgrade\",\n]);\n\nexport type Surface = \"merge\" | \"operator\";\n\n/** Finding-id prefixes that touch a PRIVILEGED surface — CI/CD, lockfiles, hooks,\n * checker config, graders/evals, test integrity, git history (SPEC-0073 R5). These\n * render first in the Agent Work Record, ▲-marked, never collapsed: an edit here runs\n * with repository secrets or moves the ground truth reviewers stand on. */\nexport const PRIVILEGED_PREFIXES = [\n \"ci-cd-touch\",\n \"lockfile-edit\",\n \"hook-bypass\",\n \"config-weaken\",\n \"spec-tamper\",\n \"grader-edit\",\n \"eval-override\",\n \"test-focus\",\n \"test-skipped\",\n \"test-trivialised\",\n \"green-by-suppression\",\n \"untested-test\",\n \"history-rewrite\",\n \"force-push\",\n \"self-permission\",\n \"sensitive-access\",\n \"guard-blocked\",\n \"mcp-write\",\n \"mcp-config-change\",\n \"metadata-egress\",\n \"tls-verify-disabled\",\n \"setuid-create\",\n \"obfuscated-exec\",\n \"untrusted-dep-source\",\n \"persistence-write\",\n \"masked-failure\",\n];\n\n/** `file-shrink` is privileged only when the shrunk file is a test (assertion loss). */\nconst TEST_PATH = /(?:^|\\/)(?:tests?|specs?|__tests__)(?:\\/|$)|\\.(?:test|spec)\\.|_test\\./;\n\nexport function privileged(id: string, filePath?: string): boolean {\n if (PRIVILEGED_PREFIXES.some((p) => id.startsWith(p))) {\n return true;\n }\n return id.startsWith(\"file-shrink\") && !!filePath && TEST_PATH.test(filePath);\n}\n\nexport function findingSurface(id: string): Surface {\n // Classify on the BASE kind: strip a subagent suffix (`-sa-<agentId>`, M104) so a wrapped operator\n // id like `bottleneck-sa-1a2b3c4d` is still recognized as operator and can't leak onto the merge\n // surface. (`errcluster-<tool>` — \"X failed N× before succeeding\" — is also an efficiency signal.)\n const base = id.replace(/-sa-[0-9a-f]+$/i, \"\");\n if (OPERATOR_KINDS.has(base) || base.startsWith(\"errcluster-\")) {\n return \"operator\";\n }\n return \"merge\";\n}\n\n/**\n * Shared shape for the destructive-op carve-outs below. Parses the `rm`/`rmdir` clause from a\n * `Destructive op:` finding title and returns true only when EVERY removal operand satisfies\n * `safe`. A non-rm destructive (git/SQL/infra acts on the repo or an external system) or an empty\n * operand list returns false — i.e. the finding keeps flagging. Each carve-out below just supplies\n * the per-operand `safe` predicate.\n */\nfunction everyRemovalOperand(title: string, safe: (operand: string) => boolean): boolean {\n const clause = title.replace(/^Destructive op:\\s*/i, \"\").replace(/\\s*×\\d+\\s*$/, \"\");\n const m = clause.match(/^\\s*(rm|rmdir)\\b(.*)$/i);\n if (!m) {\n return false;\n }\n const operands = m[2].split(/\\s+/).filter((t) => t && !t.startsWith(\"-\"));\n return operands.length > 0 && operands.every(safe);\n}\n\n/** Strip surrounding quotes from a parsed operand before matching. */\nconst unquote = (operand: string): string => operand.replace(/^[\"']|[\"']$/g, \"\");\n\n/**\n * True when a destructive finding's target is a **scratch path** (a `$VAR`, `/tmp`,\n * `/var/folders`, or `~`), so it can't be a merge risk and shouldn't gate the PR. Conservative:\n * `rm -rf ./src`, `rm -rf build`, `git reset --hard`, `DROP TABLE` are all kept (return false).\n */\nexport function destructiveOutsideRepo(title: string): boolean {\n return everyRemovalOperand(title, (operand) => {\n const p = unquote(operand);\n return (\n /\\$/.test(p) ||\n p.startsWith(\"/tmp\") ||\n p.startsWith(\"/var/folders\") ||\n p.startsWith(\"/private/var/folders\") ||\n p.startsWith(\"~\")\n );\n });\n}\n\n/**\n * True when a destructive finding deletes only the tool's **own regenerable Receipt artifact**\n * (`rm .receipts/<branch>.json`) — a scope-layer carve-out: a `.receipts/*.json` is inside the repo\n * but is rewritten by `receipts pr`, so it isn't a merge risk. Only a flat `.receipts/<name>.json`\n * file matches; a mixed command or the whole dir (`rm -rf .receipts`) keeps flagging.\n */\nexport function regenerableReceiptRm(title: string): boolean {\n return everyRemovalOperand(title, (operand) =>\n /(?:^|\\/)\\.receipts\\/[^/]+\\.json$/.test(unquote(operand)),\n );\n}\n\n/** Well-known regenerable build/dependency output dirs — deleting them is housekeeping, not\n * data loss. Frozen + conservative: excludes `vendor` (committed in Go/PHP), `.cache`/`bin`\n * (ambiguous), `.terraform` (stateful). */\nconst BUILD_DIRS = new Set([\n \"target\",\n \"dist\",\n \"build\",\n \"out\",\n \"node_modules\",\n \".next\",\n \".nuxt\",\n \".turbo\",\n \".parcel-cache\",\n \"__pycache__\",\n \".pytest_cache\",\n \".mypy_cache\",\n \".ruff_cache\",\n \"coverage\",\n \".gradle\",\n]);\n\n/**\n * True when a destructive op deletes only **regenerable build artifacts** (`rm -rf dist`, `rm -rf\n * node_modules`). Scope-layer carve-out like {@link destructiveOutsideRepo}: build output is inside\n * the repo but regenerable in one command. Conservative: every operand must have a path segment in\n * the frozen build-dir set — a mixed command (`rm -rf dist src`) or a real target keeps flagging.\n */\nexport function regenerableBuildArtifact(title: string): boolean {\n return everyRemovalOperand(title, (operand) =>\n unquote(operand)\n .split(\"/\")\n .some((seg) => BUILD_DIRS.has(seg)),\n );\n}\n"],"mappings":";;;AASA,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQM,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,YAAY;AAEX,SAAS,WAAW,IAAY,UAA4B;AACjE,MAAI,oBAAoB,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,GAAG;AACrD,WAAO;AAAA,EACT;AACA,SAAO,GAAG,WAAW,aAAa,KAAK,CAAC,CAAC,YAAY,UAAU,KAAK,QAAQ;AAC9E;AAEO,SAAS,eAAe,IAAqB;AAIlD,QAAM,OAAO,GAAG,QAAQ,mBAAmB,EAAE;AAC7C,MAAI,eAAe,IAAI,IAAI,KAAK,KAAK,WAAW,aAAa,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASA,SAAS,oBAAoB,OAAe,MAA6C;AACvF,QAAM,SAAS,MAAM,QAAQ,wBAAwB,EAAE,EAAE,QAAQ,eAAe,EAAE;AAClF,QAAM,IAAI,OAAO,MAAM,wBAAwB;AAC/C,MAAI,CAAC,GAAG;AACN,WAAO;AAAA,EACT;AACA,QAAM,WAAW,EAAE,CAAC,EAAE,MAAM,KAAK,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,WAAW,GAAG,CAAC;AACxE,SAAO,SAAS,SAAS,KAAK,SAAS,MAAM,IAAI;AACnD;AAGA,IAAM,UAAU,CAAC,YAA4B,QAAQ,QAAQ,gBAAgB,EAAE;AAOxE,SAAS,uBAAuB,OAAwB;AAC7D,SAAO,oBAAoB,OAAO,CAAC,YAAY;AAC7C,UAAM,IAAI,QAAQ,OAAO;AACzB,WACE,KAAK,KAAK,CAAC,KACX,EAAE,WAAW,MAAM,KACnB,EAAE,WAAW,cAAc,KAC3B,EAAE,WAAW,sBAAsB,KACnC,EAAE,WAAW,GAAG;AAAA,EAEpB,CAAC;AACH;AAQO,SAAS,qBAAqB,OAAwB;AAC3D,SAAO;AAAA,IAAoB;AAAA,IAAO,CAAC,YACjC,mCAAmC,KAAK,QAAQ,OAAO,CAAC;AAAA,EAC1D;AACF;AAKA,IAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAQM,SAAS,yBAAyB,OAAwB;AAC/D,SAAO;AAAA,IAAoB;AAAA,IAAO,CAAC,YACjC,QAAQ,OAAO,EACZ,MAAM,GAAG,EACT,KAAK,CAAC,QAAQ,WAAW,IAAI,GAAG,CAAC;AAAA,EACtC;AACF;","names":[]}
|
|
@@ -3,7 +3,21 @@ import {
|
|
|
3
3
|
destructiveOutsideRepo,
|
|
4
4
|
regenerableBuildArtifact,
|
|
5
5
|
regenerableReceiptRm
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-44V3DNK5.js";
|
|
7
|
+
|
|
8
|
+
// src/version.ts
|
|
9
|
+
import { readFileSync } from "fs";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
function getVersion() {
|
|
12
|
+
try {
|
|
13
|
+
const pkgUrl = new URL("../package.json", import.meta.url);
|
|
14
|
+
const raw = readFileSync(fileURLToPath(pkgUrl), "utf8");
|
|
15
|
+
const pkg = JSON.parse(raw);
|
|
16
|
+
return pkg.version ?? "0.0.0";
|
|
17
|
+
} catch {
|
|
18
|
+
return "0.0.0";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
7
21
|
|
|
8
22
|
// src/findings/gitParse.ts
|
|
9
23
|
var WRAPPER = /* @__PURE__ */ new Set(["sudo", "env", "nohup", "command", "stdbuf", "time", "timeout", "xargs"]);
|
|
@@ -2814,6 +2828,48 @@ function deriveMcpFindings(sum) {
|
|
|
2814
2828
|
}
|
|
2815
2829
|
];
|
|
2816
2830
|
}
|
|
2831
|
+
var MCP_CONFIG_FILE = /(?:^|\/)\.mcp\.json$|(?:^|\/)\.(?:cursor|vscode)\/mcp\.json$|(?:^|\/)claude_desktop_config\.json$|(?:^|\/)mcp\.json$/i;
|
|
2832
|
+
var SERVER_ENDPOINT = /"(?:command|url)"\s*:\s*"((?:[^"\\]|\\.)*)"/g;
|
|
2833
|
+
var endpointValues = (text) => {
|
|
2834
|
+
const out = [];
|
|
2835
|
+
for (const m of text.matchAll(SERVER_ENDPOINT)) {
|
|
2836
|
+
out.push(m[1]);
|
|
2837
|
+
}
|
|
2838
|
+
return out;
|
|
2839
|
+
};
|
|
2840
|
+
function deriveMcpConfigFindings(sum) {
|
|
2841
|
+
for (const s of sum.spans) {
|
|
2842
|
+
if (s.kind !== "tool" || typeof s.name !== "string" || !isEditTool(s.name)) {
|
|
2843
|
+
continue;
|
|
2844
|
+
}
|
|
2845
|
+
const fp = filePathOf(s.input);
|
|
2846
|
+
if (!fp || !MCP_CONFIG_FILE.test(fp)) {
|
|
2847
|
+
continue;
|
|
2848
|
+
}
|
|
2849
|
+
const { oldStr, newStr } = editBody(s.input);
|
|
2850
|
+
const before = new Set(endpointValues(oldStr));
|
|
2851
|
+
const added = endpointValues(newStr).filter((v) => !before.has(v));
|
|
2852
|
+
if (added.length === 0) {
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
const base6 = fp.split("/").pop() ?? fp;
|
|
2856
|
+
return [
|
|
2857
|
+
{
|
|
2858
|
+
id: `mcp-config-change-${s.spanId}`,
|
|
2859
|
+
severity: "high",
|
|
2860
|
+
title: `Added or repointed an MCP server in ${base6}`,
|
|
2861
|
+
detail: `An edit to \`${base6}\` introduced or changed an MCP server endpoint (\`${added[0]}\`) \u2014 it grants the agent (and future agents on this branch) a new external tool surface with whatever credentials that server holds, which the source diff doesn't otherwise show. Review the new server's source and scopes before merging.`,
|
|
2862
|
+
impactLabel: "MCP authority change",
|
|
2863
|
+
confidence: 0.85,
|
|
2864
|
+
score: 100 * 0.85,
|
|
2865
|
+
evidenceSpanId: s.spanId,
|
|
2866
|
+
filePath: fp,
|
|
2867
|
+
guardrailRule: "Adding or repointing an MCP server expands the agent's authority \u2014 review the server's source and scopes as part of the change."
|
|
2868
|
+
}
|
|
2869
|
+
];
|
|
2870
|
+
}
|
|
2871
|
+
return [];
|
|
2872
|
+
}
|
|
2817
2873
|
|
|
2818
2874
|
// src/findings/persistenceFindings.ts
|
|
2819
2875
|
var AUTORUN_PATH = /(?:^|\/)\.(?:bashrc|zshrc|bash_profile|zprofile|profile|bash_login|zshenv)$|(?:^|\/)\.config\/fish\/config\.fish$|^\/etc\/profile(?:\.d\/.+)?$|(?:^|\/)Library\/LaunchAgents\/[^/]+|^\/Library\/Launch(?:Daemons|Agents)\/[^/]+|^\/etc\/systemd\/system\/[^/]+|(?:^|\/)\.config\/systemd\/user\/[^/]+|^\/etc\/cron[^/]*|^\/var\/spool\/cron\//i;
|
|
@@ -3093,6 +3149,8 @@ var DETECTORS = [
|
|
|
3093
3149
|
{ name: "sensitive-access", fn: (s) => deriveSensitiveAccess(s) },
|
|
3094
3150
|
// MCP external side-effects (M105): mutating MCP tool calls the diff can't show
|
|
3095
3151
|
{ name: "mcp", fn: (s) => deriveMcpFindings(s) },
|
|
3152
|
+
// MCP config change (M113): a new/repointed MCP server in a config edit — authority expansion
|
|
3153
|
+
{ name: "mcp-config", fn: (s) => deriveMcpConfigFindings(s) },
|
|
3096
3154
|
// command-pattern security (M106/108/109/110/111/112): metadata egress, untrusted dep source,
|
|
3097
3155
|
// TLS-disable, setuid, obfuscated exec, masked verification failure
|
|
3098
3156
|
{ name: "command-security", fn: (s) => deriveCommandSecurityFindings(s) },
|
|
@@ -3591,20 +3649,6 @@ function applyDiffScope(derived, findings, files, projectPath) {
|
|
|
3591
3649
|
};
|
|
3592
3650
|
}
|
|
3593
3651
|
|
|
3594
|
-
// src/version.ts
|
|
3595
|
-
import { readFileSync } from "fs";
|
|
3596
|
-
import { fileURLToPath } from "url";
|
|
3597
|
-
function getVersion() {
|
|
3598
|
-
try {
|
|
3599
|
-
const pkgUrl = new URL("../package.json", import.meta.url);
|
|
3600
|
-
const raw = readFileSync(fileURLToPath(pkgUrl), "utf8");
|
|
3601
|
-
const pkg = JSON.parse(raw);
|
|
3602
|
-
return pkg.version ?? "0.0.0";
|
|
3603
|
-
} catch {
|
|
3604
|
-
return "0.0.0";
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
|
|
3608
3652
|
// src/receipt/hash.ts
|
|
3609
3653
|
import { createHash } from "crypto";
|
|
3610
3654
|
import { promises as fs } from "fs";
|
|
@@ -5634,6 +5678,7 @@ function redactReceipt(receipt) {
|
|
|
5634
5678
|
}
|
|
5635
5679
|
|
|
5636
5680
|
export {
|
|
5681
|
+
getVersion,
|
|
5637
5682
|
deriveSpans,
|
|
5638
5683
|
cwdAtFirstGit,
|
|
5639
5684
|
formatTokens,
|
|
@@ -5646,7 +5691,6 @@ export {
|
|
|
5646
5691
|
narrowEffort,
|
|
5647
5692
|
windowedEffort,
|
|
5648
5693
|
applyDiffScope,
|
|
5649
|
-
getVersion,
|
|
5650
5694
|
hashTranscriptFile,
|
|
5651
5695
|
sha256Hex,
|
|
5652
5696
|
PREDICATE_TYPE,
|
|
@@ -5675,4 +5719,4 @@ export {
|
|
|
5675
5719
|
redact,
|
|
5676
5720
|
redactReceipt
|
|
5677
5721
|
};
|
|
5678
|
-
//# sourceMappingURL=chunk-
|
|
5722
|
+
//# sourceMappingURL=chunk-C7NAN2LL.js.map
|