tamperward 2.37.0 → 2.37.2
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/cli/index.js +477 -169
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -5300,13 +5300,13 @@ function merge(a, b) {
|
|
|
5300
5300
|
function literals(e) {
|
|
5301
5301
|
if (ts.isStringLiteral(e)) return { items: [e.text], opaque: false };
|
|
5302
5302
|
if (!ts.isArrayLiteralExpression(e)) return { items: [], opaque: true };
|
|
5303
|
-
const
|
|
5303
|
+
const items2 = [];
|
|
5304
5304
|
let opaque = false;
|
|
5305
5305
|
for (const el of e.elements) {
|
|
5306
|
-
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el))
|
|
5306
|
+
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) items2.push(el.text);
|
|
5307
5307
|
else opaque = true;
|
|
5308
5308
|
}
|
|
5309
|
-
return { items, opaque };
|
|
5309
|
+
return { items: items2, opaque };
|
|
5310
5310
|
}
|
|
5311
5311
|
function parseLists(src) {
|
|
5312
5312
|
const res = { collectFrom: null, collectKey: "collectCoverageFrom", ignore: [], ignoreRe: [], opaqueCollect: false, opaqueIgnore: false };
|
|
@@ -5319,13 +5319,13 @@ function parseLists(src) {
|
|
|
5319
5319
|
const k = keyName(node.name);
|
|
5320
5320
|
const vitestCoverage = ownerKey(node) === "coverage";
|
|
5321
5321
|
if (k === "collectCoverageFrom" || k === "include" && vitestCoverage) {
|
|
5322
|
-
const { items, opaque } = literals(resolve23(node.initializer));
|
|
5323
|
-
res.collectFrom =
|
|
5322
|
+
const { items: items2, opaque } = literals(resolve23(node.initializer));
|
|
5323
|
+
res.collectFrom = items2;
|
|
5324
5324
|
res.collectKey = k === "include" ? "coverage.include" : k;
|
|
5325
5325
|
if (opaque) res.opaqueCollect = true;
|
|
5326
5326
|
} else if (k === "coveragePathIgnorePatterns" || k === "exclude" && vitestCoverage) {
|
|
5327
|
-
const { items, opaque } = literals(resolve23(node.initializer));
|
|
5328
|
-
(k === "exclude" ? res.ignore : res.ignoreRe).push(...
|
|
5327
|
+
const { items: items2, opaque } = literals(resolve23(node.initializer));
|
|
5328
|
+
(k === "exclude" ? res.ignore : res.ignoreRe).push(...items2);
|
|
5329
5329
|
if (opaque) res.opaqueIgnore = true;
|
|
5330
5330
|
}
|
|
5331
5331
|
}
|
|
@@ -5923,15 +5923,15 @@ function keyName2(name) {
|
|
|
5923
5923
|
function literals2(e) {
|
|
5924
5924
|
if (isStr(e)) return { items: [e.text], opaque: false };
|
|
5925
5925
|
if (!ts.isArrayLiteralExpression(e)) return { items: [], opaque: true };
|
|
5926
|
-
const
|
|
5926
|
+
const items2 = [];
|
|
5927
5927
|
let opaque = false;
|
|
5928
5928
|
for (const el of e.elements) {
|
|
5929
|
-
if (isStr(el))
|
|
5929
|
+
if (isStr(el)) items2.push(el.text);
|
|
5930
5930
|
else if (ts.isSpreadElement(el) && /^(?:configDefaults\.|default)(?:exclude|include)$/i.test(el.expression.getText())) {
|
|
5931
|
-
|
|
5931
|
+
items2.push(.../exclude$/i.test(el.expression.getText()) ? VITEST_DEFAULT_EXCLUDE : VITEST_DEFAULT_INCLUDE);
|
|
5932
5932
|
} else opaque = true;
|
|
5933
5933
|
}
|
|
5934
|
-
return { items, opaque };
|
|
5934
|
+
return { items: items2, opaque };
|
|
5935
5935
|
}
|
|
5936
5936
|
function ownerKey2(node) {
|
|
5937
5937
|
const obj = node.parent;
|
|
@@ -6054,11 +6054,11 @@ function collect(root, runner) {
|
|
|
6054
6054
|
const sel = empty();
|
|
6055
6055
|
const seen = /* @__PURE__ */ new Set();
|
|
6056
6056
|
const take = (key2, e) => {
|
|
6057
|
-
const { items, opaque } = literals2(e);
|
|
6057
|
+
const { items: items2, opaque } = literals2(e);
|
|
6058
6058
|
if (opaque || seen.has(key2)) sel.opaque = true;
|
|
6059
6059
|
seen.add(key2);
|
|
6060
6060
|
sel.present = true;
|
|
6061
|
-
return
|
|
6061
|
+
return items2.map(stripRootDir);
|
|
6062
6062
|
};
|
|
6063
6063
|
const ignores = (key2, e) => {
|
|
6064
6064
|
const entries = take(key2, e).map((pattern) => ({ pattern, key: key2 }));
|
|
@@ -10701,10 +10701,10 @@ function validate(r, where) {
|
|
|
10701
10701
|
throw new PolicyError(`${where}: ${msg}`);
|
|
10702
10702
|
}
|
|
10703
10703
|
const show = (v) => JSON.stringify(v) ?? String(v);
|
|
10704
|
-
function refuseNegated(
|
|
10704
|
+
function refuseNegated(field2, list3) {
|
|
10705
10705
|
const neg = list3.find(isNegatedGlob);
|
|
10706
10706
|
if (neg !== void 0) {
|
|
10707
|
-
bad2(`${
|
|
10707
|
+
bad2(`${field2}: negated glob ${show(neg)} is not allowed \u2014 a "!" pattern matches every path, so it would ${field2 === "ignore" ? "ignore every file" : field2.startsWith("protected.") ? `put every file in ${field2}` : "blind the rule on every file"}; list the paths to include instead`);
|
|
10708
10708
|
}
|
|
10709
10709
|
}
|
|
10710
10710
|
const out3 = {};
|
|
@@ -12201,7 +12201,7 @@ var init_fs_events = __esm({
|
|
|
12201
12201
|
|
|
12202
12202
|
// src/cli/audit.ts
|
|
12203
12203
|
import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
12204
|
-
import { appendFileSync as appendFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync5,
|
|
12204
|
+
import { appendFileSync as appendFileSync4, closeSync as closeSync3, existsSync as existsSync9, mkdirSync as mkdirSync5, openSync as openSync3, readSync as readSync3 } from "node:fs";
|
|
12205
12205
|
import { dirname as dirname4, isAbsolute as isAbsolute8, join as join13, resolve as resolve4 } from "node:path";
|
|
12206
12206
|
function sessionHash(sessionId) {
|
|
12207
12207
|
if (!sessionId) return void 0;
|
|
@@ -12311,19 +12311,56 @@ function parseAuditEvent(value, line = 0) {
|
|
|
12311
12311
|
...typeof value.session === "string" ? { session: value.session } : {}
|
|
12312
12312
|
};
|
|
12313
12313
|
}
|
|
12314
|
-
function
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12314
|
+
function parseAuditLine(line, lineNumber) {
|
|
12315
|
+
let value;
|
|
12316
|
+
try {
|
|
12317
|
+
value = JSON.parse(line);
|
|
12318
|
+
} catch {
|
|
12319
|
+
throw new Error(`audit line ${lineNumber} is not valid JSON`);
|
|
12320
|
+
}
|
|
12321
|
+
return parseAuditEvent(value, lineNumber);
|
|
12322
|
+
}
|
|
12323
|
+
function forEachAuditLine(file, onLine, maxLineBytes = MAX_AUDIT_LINE_BYTES) {
|
|
12324
|
+
const fd = openSync3(file, "r");
|
|
12325
|
+
try {
|
|
12326
|
+
const chunk = Buffer.allocUnsafe(64 * 1024);
|
|
12327
|
+
let pending = [];
|
|
12328
|
+
let pendingBytes = 0;
|
|
12329
|
+
let lineNumber = 0;
|
|
12330
|
+
const emit3 = (parts) => {
|
|
12331
|
+
lineNumber++;
|
|
12332
|
+
let line = parts.length === 1 ? parts[0].toString("utf8") : Buffer.concat(parts).toString("utf8");
|
|
12333
|
+
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
12334
|
+
if (line.trim()) onLine(line, lineNumber);
|
|
12335
|
+
};
|
|
12336
|
+
for (; ; ) {
|
|
12337
|
+
const read = readSync3(fd, chunk, 0, chunk.length, null);
|
|
12338
|
+
if (read === 0) break;
|
|
12339
|
+
let start = 0;
|
|
12340
|
+
for (let i = 0; i < read; i++) {
|
|
12341
|
+
if (chunk[i] !== 10) continue;
|
|
12342
|
+
const part = chunk.subarray(start, i);
|
|
12343
|
+
if (pendingBytes + part.length > maxLineBytes) {
|
|
12344
|
+
throw new Error(`audit line ${lineNumber + 1} exceeds ${maxLineBytes} bytes (not an audit-v1 event)`);
|
|
12345
|
+
}
|
|
12346
|
+
emit3(pending.length ? [...pending, part] : [part]);
|
|
12347
|
+
pending = [];
|
|
12348
|
+
pendingBytes = 0;
|
|
12349
|
+
start = i + 1;
|
|
12350
|
+
}
|
|
12351
|
+
if (start < read) {
|
|
12352
|
+
const rest = Buffer.from(chunk.subarray(start, read));
|
|
12353
|
+
pendingBytes += rest.length;
|
|
12354
|
+
if (pendingBytes > maxLineBytes) {
|
|
12355
|
+
throw new Error(`audit line ${lineNumber + 1} exceeds ${maxLineBytes} bytes (not an audit-v1 event)`);
|
|
12356
|
+
}
|
|
12357
|
+
pending.push(rest);
|
|
12358
|
+
}
|
|
12323
12359
|
}
|
|
12324
|
-
|
|
12325
|
-
}
|
|
12326
|
-
|
|
12360
|
+
if (pending.length) emit3(pending);
|
|
12361
|
+
} finally {
|
|
12362
|
+
closeSync3(fd);
|
|
12363
|
+
}
|
|
12327
12364
|
}
|
|
12328
12365
|
function parseSince(value, nowMs = Date.now()) {
|
|
12329
12366
|
const relative13 = /^(\d+)(m|h|d)$/.exec(value);
|
|
@@ -12339,37 +12376,58 @@ function parseSince(value, nowMs = Date.now()) {
|
|
|
12339
12376
|
}
|
|
12340
12377
|
return parsed;
|
|
12341
12378
|
}
|
|
12342
|
-
function
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12379
|
+
function createAuditAggregator() {
|
|
12380
|
+
let events = 0;
|
|
12381
|
+
let blocked = 0;
|
|
12382
|
+
let warnings = 0;
|
|
12346
12383
|
const rules = /* @__PURE__ */ new Map();
|
|
12347
12384
|
const surfaces = /* @__PURE__ */ new Map();
|
|
12348
12385
|
const sessions = /* @__PURE__ */ new Set();
|
|
12349
|
-
|
|
12350
|
-
const bucket = rules.get(event.rule) ?? { events: 0, blocked: 0, warnings: 0 };
|
|
12351
|
-
bucket.events++;
|
|
12352
|
-
if (event.severity === "block") bucket.blocked++;
|
|
12353
|
-
else bucket.warnings++;
|
|
12354
|
-
rules.set(event.rule, bucket);
|
|
12355
|
-
surfaces.set(event.surface, (surfaces.get(event.surface) ?? 0) + 1);
|
|
12356
|
-
if (event.session) sessions.add(event.session);
|
|
12357
|
-
}
|
|
12358
|
-
const by_rule = [...rules.entries()].map(([rule, counts]) => ({ rule, ...counts })).sort((a, b) => b.events - a.events || a.rule.localeCompare(b.rule));
|
|
12359
|
-
const by_surface = [...surfaces.entries()].map(([surface, count]) => ({ surface, events: count })).sort((a, b) => b.events - a.events || a.surface.localeCompare(b.surface));
|
|
12386
|
+
const bounds = { first: null, last: null };
|
|
12360
12387
|
return {
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12388
|
+
add(event) {
|
|
12389
|
+
events++;
|
|
12390
|
+
if (event.severity === "block") blocked++;
|
|
12391
|
+
else warnings++;
|
|
12392
|
+
const bucket = rules.get(event.rule) ?? { events: 0, blocked: 0, warnings: 0 };
|
|
12393
|
+
bucket.events++;
|
|
12394
|
+
if (event.severity === "block") bucket.blocked++;
|
|
12395
|
+
else bucket.warnings++;
|
|
12396
|
+
rules.set(event.rule, bucket);
|
|
12397
|
+
surfaces.set(event.surface, (surfaces.get(event.surface) ?? 0) + 1);
|
|
12398
|
+
if (event.session) sessions.add(event.session);
|
|
12399
|
+
const ms = Date.parse(event.timestamp);
|
|
12400
|
+
const { first, last } = bounds;
|
|
12401
|
+
if (first === null || ms < first.ms || ms === first.ms && event.id.localeCompare(first.id) < 0) {
|
|
12402
|
+
bounds.first = { ms, id: event.id, timestamp: event.timestamp };
|
|
12403
|
+
}
|
|
12404
|
+
if (last === null || ms > last.ms || ms === last.ms && event.id.localeCompare(last.id) > 0) {
|
|
12405
|
+
bounds.last = { ms, id: event.id, timestamp: event.timestamp };
|
|
12406
|
+
}
|
|
12407
|
+
},
|
|
12408
|
+
finish() {
|
|
12409
|
+
const by_rule = [...rules.entries()].map(([rule, counts]) => ({ rule, ...counts })).sort((a, b) => b.events - a.events || a.rule.localeCompare(b.rule));
|
|
12410
|
+
const by_surface = [...surfaces.entries()].map(([surface, count]) => ({ surface, events: count })).sort((a, b) => b.events - a.events || a.surface.localeCompare(b.surface));
|
|
12411
|
+
return {
|
|
12412
|
+
schema_version: AUDIT_SCHEMA_VERSION,
|
|
12413
|
+
events,
|
|
12414
|
+
blocked,
|
|
12415
|
+
warnings,
|
|
12416
|
+
sessions: sessions.size,
|
|
12417
|
+
first_event: bounds.first === null ? null : bounds.first.timestamp,
|
|
12418
|
+
last_event: bounds.last === null ? null : bounds.last.timestamp,
|
|
12419
|
+
by_rule,
|
|
12420
|
+
by_surface,
|
|
12421
|
+
interpretation: "finding-is-not-proof-of-intent"
|
|
12422
|
+
};
|
|
12423
|
+
}
|
|
12371
12424
|
};
|
|
12372
12425
|
}
|
|
12426
|
+
function summarizeAudit(events) {
|
|
12427
|
+
const aggregator = createAuditAggregator();
|
|
12428
|
+
for (const event of events) aggregator.add(event);
|
|
12429
|
+
return aggregator.finish();
|
|
12430
|
+
}
|
|
12373
12431
|
function renderAuditStats(summary) {
|
|
12374
12432
|
const out3 = [
|
|
12375
12433
|
"TamperWard audit stats",
|
|
@@ -12408,21 +12466,24 @@ function runStats(opts = {}) {
|
|
|
12408
12466
|
process.stdout.write(opts.json ? JSON.stringify(empty2) + "\n" : renderAuditStats(empty2));
|
|
12409
12467
|
return 0;
|
|
12410
12468
|
}
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12469
|
+
const aggregator = createAuditAggregator();
|
|
12470
|
+
forEachAuditLine(file, (line, lineNumber) => {
|
|
12471
|
+
const event = parseAuditLine(line, lineNumber);
|
|
12472
|
+
if (cutoff !== null && Date.parse(event.timestamp) < cutoff) return;
|
|
12473
|
+
aggregator.add(event);
|
|
12474
|
+
});
|
|
12475
|
+
const summary = aggregator.finish();
|
|
12416
12476
|
process.stdout.write(opts.json ? JSON.stringify(summary) + "\n" : renderAuditStats(summary));
|
|
12417
12477
|
return 0;
|
|
12418
12478
|
}
|
|
12419
|
-
var AUDIT_SCHEMA_VERSION, AUDIT_LOG_ENV, ALLOWED_KEYS;
|
|
12479
|
+
var AUDIT_SCHEMA_VERSION, AUDIT_LOG_ENV, MAX_AUDIT_LINE_BYTES, ALLOWED_KEYS;
|
|
12420
12480
|
var init_audit = __esm({
|
|
12421
12481
|
"src/cli/audit.ts"() {
|
|
12422
12482
|
"use strict";
|
|
12423
12483
|
init_repo_context();
|
|
12424
12484
|
AUDIT_SCHEMA_VERSION = 1;
|
|
12425
12485
|
AUDIT_LOG_ENV = "TAMPERWARD_AUDIT_LOG";
|
|
12486
|
+
MAX_AUDIT_LINE_BYTES = 16 * 1024;
|
|
12426
12487
|
ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
12427
12488
|
"schema_version",
|
|
12428
12489
|
"id",
|
|
@@ -12438,14 +12499,14 @@ var init_audit = __esm({
|
|
|
12438
12499
|
});
|
|
12439
12500
|
|
|
12440
12501
|
// src/cli/hook.ts
|
|
12441
|
-
import { readFileSync as
|
|
12502
|
+
import { readFileSync as readFileSync10, appendFileSync as appendFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "node:fs";
|
|
12442
12503
|
import { createHash as createHash7 } from "node:crypto";
|
|
12443
12504
|
function setSnapshotCache(cache3) {
|
|
12444
12505
|
snapshotCache = cache3;
|
|
12445
12506
|
}
|
|
12446
12507
|
function readStdin() {
|
|
12447
12508
|
try {
|
|
12448
|
-
return
|
|
12509
|
+
return readFileSync10(0, "utf8");
|
|
12449
12510
|
} catch (e) {
|
|
12450
12511
|
throw new HookInputError(`cannot read the hook payload on stdin: ${e instanceof Error ? e.message : String(e)}`);
|
|
12451
12512
|
}
|
|
@@ -12738,7 +12799,7 @@ function turnTransientBlocks(cwd, sessionId, policy, changes) {
|
|
|
12738
12799
|
if (!existsSync10(log)) return none;
|
|
12739
12800
|
let offset = 0;
|
|
12740
12801
|
if (cp && existsSync10(cp)) {
|
|
12741
|
-
const n2 = Number(
|
|
12802
|
+
const n2 = Number(readFileSync10(cp, "utf8"));
|
|
12742
12803
|
offset = Number.isFinite(n2) ? n2 : 0;
|
|
12743
12804
|
}
|
|
12744
12805
|
const drained = drainEvents(log, offset);
|
|
@@ -13012,13 +13073,13 @@ var init_ptree_cache = __esm({
|
|
|
13012
13073
|
});
|
|
13013
13074
|
|
|
13014
13075
|
// src/cli/hook-service.ts
|
|
13015
|
-
import { chmodSync, existsSync as existsSync11, lstatSync as lstatSync6, mkdirSync as mkdirSync6, readFileSync as
|
|
13076
|
+
import { chmodSync, existsSync as existsSync11, lstatSync as lstatSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync11, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
13016
13077
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
13017
13078
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
13018
13079
|
import { sep } from "node:path";
|
|
13019
13080
|
function readServiceState(paths) {
|
|
13020
13081
|
try {
|
|
13021
|
-
const v = JSON.parse(
|
|
13082
|
+
const v = JSON.parse(readFileSync11(paths.state, "utf8"));
|
|
13022
13083
|
if (!isRecord(v)) return null;
|
|
13023
13084
|
const { pid, version, root, started_at } = v;
|
|
13024
13085
|
if (typeof pid !== "number" || typeof version !== "string" || typeof root !== "string" || typeof started_at !== "string") return null;
|
|
@@ -13579,7 +13640,7 @@ var init_safe_write = __esm({
|
|
|
13579
13640
|
// src/cli/init.ts
|
|
13580
13641
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
13581
13642
|
import { createHash as createHash8 } from "node:crypto";
|
|
13582
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as
|
|
13643
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync12, realpathSync as realpathSync5 } from "node:fs";
|
|
13583
13644
|
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve6 } from "node:path";
|
|
13584
13645
|
function gitConfig(cwd, key2) {
|
|
13585
13646
|
try {
|
|
@@ -13673,34 +13734,34 @@ function verifierCandidates(cwd) {
|
|
|
13673
13734
|
if (!candidates.includes(command)) candidates.push(command);
|
|
13674
13735
|
};
|
|
13675
13736
|
try {
|
|
13676
|
-
const pkg = JSON.parse(
|
|
13737
|
+
const pkg = JSON.parse(readFileSync12(join15(cwd, "package.json"), "utf8"));
|
|
13677
13738
|
const test = isRecord(pkg) && isRecord(pkg.scripts) ? pkg.scripts.test : void 0;
|
|
13678
13739
|
if (typeof test === "string" && test.trim() !== "" && !/error:\s*no test specified/i.test(test)) add("npm test");
|
|
13679
13740
|
} catch {
|
|
13680
13741
|
}
|
|
13681
13742
|
try {
|
|
13682
|
-
const pyproject =
|
|
13743
|
+
const pyproject = readFileSync12(join15(cwd, "pyproject.toml"), "utf8");
|
|
13683
13744
|
if (/^\s*\[tool\.pytest(?:\.ini_options)?\]\s*$/m.test(pyproject)) add("pytest");
|
|
13684
13745
|
} catch {
|
|
13685
13746
|
}
|
|
13686
13747
|
if (existsSync12(join15(cwd, "pytest.ini"))) add("pytest");
|
|
13687
13748
|
try {
|
|
13688
|
-
const setupCfg =
|
|
13749
|
+
const setupCfg = readFileSync12(join15(cwd, "setup.cfg"), "utf8");
|
|
13689
13750
|
if (/^\s*\[tool:pytest\]\s*$/m.test(setupCfg)) add("pytest");
|
|
13690
13751
|
} catch {
|
|
13691
13752
|
}
|
|
13692
13753
|
try {
|
|
13693
|
-
const tox =
|
|
13754
|
+
const tox = readFileSync12(join15(cwd, "tox.ini"), "utf8");
|
|
13694
13755
|
if (/^\s*\[tox\]\s*$/m.test(tox)) add("tox");
|
|
13695
13756
|
} catch {
|
|
13696
13757
|
}
|
|
13697
13758
|
try {
|
|
13698
|
-
const cargo =
|
|
13759
|
+
const cargo = readFileSync12(join15(cwd, "Cargo.toml"), "utf8");
|
|
13699
13760
|
if (/^\s*\[(?:package|workspace)\]\s*(?:#.*)?$/m.test(cargo)) add("cargo test");
|
|
13700
13761
|
} catch {
|
|
13701
13762
|
}
|
|
13702
13763
|
try {
|
|
13703
|
-
const goMod =
|
|
13764
|
+
const goMod = readFileSync12(join15(cwd, "go.mod"), "utf8");
|
|
13704
13765
|
if (/^\s*module\s+\S+\s*(?:\/\/.*)?$/m.test(goMod)) add("go test ./...");
|
|
13705
13766
|
} catch {
|
|
13706
13767
|
}
|
|
@@ -13772,7 +13833,7 @@ function planGitignore(cwd) {
|
|
|
13772
13833
|
}
|
|
13773
13834
|
const refused = refuseNonRegular(path);
|
|
13774
13835
|
if (refused) return { item: "gitignore", path: rel, status: "error", detail: refused };
|
|
13775
|
-
if (kind === "file") existing =
|
|
13836
|
+
if (kind === "file") existing = readFileSync12(path, "utf8");
|
|
13776
13837
|
try {
|
|
13777
13838
|
const tracked = execFileSync6("git", ["ls-files", "--cached", "--", "node_modules"], {
|
|
13778
13839
|
cwd,
|
|
@@ -13830,7 +13891,7 @@ function planClaudeHooks(cwd) {
|
|
|
13830
13891
|
if (existsSync12(path)) {
|
|
13831
13892
|
let parsed;
|
|
13832
13893
|
try {
|
|
13833
|
-
parsed = JSON.parse(
|
|
13894
|
+
parsed = JSON.parse(readFileSync12(path, "utf8"));
|
|
13834
13895
|
} catch {
|
|
13835
13896
|
return {
|
|
13836
13897
|
item: "agent",
|
|
@@ -13972,7 +14033,7 @@ function planPreCommit(cwd) {
|
|
|
13972
14033
|
const rel = display(cwd, path);
|
|
13973
14034
|
const refused = refuseNonRegular(path);
|
|
13974
14035
|
if (refused) return { item: "pre-commit", path: rel, status: "error", detail: refused };
|
|
13975
|
-
const existing = existsSync12(path) ?
|
|
14036
|
+
const existing = existsSync12(path) ? readFileSync12(path, "utf8") : null;
|
|
13976
14037
|
const hookMode = () => existingMode(path, 493) | 73;
|
|
13977
14038
|
if (existing === null) {
|
|
13978
14039
|
return {
|
|
@@ -14063,7 +14124,7 @@ function planCodeowners(cwd) {
|
|
|
14063
14124
|
const path = join15(cwd, existingRel ?? rel);
|
|
14064
14125
|
const refused = refuseNonRegular(path);
|
|
14065
14126
|
if (refused) return { item: "codeowners", path: existingRel ?? rel, status: "error", detail: refused };
|
|
14066
|
-
const existing = existingRel ?
|
|
14127
|
+
const existing = existingRel ? readFileSync12(path, "utf8") : null;
|
|
14067
14128
|
const missing = CODEOWNERS_PATHS.filter((p) => existing === null || !coveredBy(existing, p));
|
|
14068
14129
|
if (existingRel !== void 0 && missing.length === 0) {
|
|
14069
14130
|
return { item: "codeowners", path: existingRel, status: "ok", detail: "gate paths already have code owners" };
|
|
@@ -14114,7 +14175,7 @@ function planWorkflow(cwd, force) {
|
|
|
14114
14175
|
}
|
|
14115
14176
|
let src;
|
|
14116
14177
|
try {
|
|
14117
|
-
src =
|
|
14178
|
+
src = readFileSync12(path, "utf8");
|
|
14118
14179
|
} catch {
|
|
14119
14180
|
return { item: "ci", path: rel, status: "error", detail: "exists but cannot be read \u2014 refusing to overwrite" };
|
|
14120
14181
|
}
|
|
@@ -14414,7 +14475,7 @@ import {
|
|
|
14414
14475
|
existsSync as existsSync13,
|
|
14415
14476
|
lstatSync as lstatSync8,
|
|
14416
14477
|
readdirSync as readdirSync3,
|
|
14417
|
-
readFileSync as
|
|
14478
|
+
readFileSync as readFileSync13,
|
|
14418
14479
|
readlinkSync as readlinkSync2,
|
|
14419
14480
|
realpathSync as realpathSync6,
|
|
14420
14481
|
statSync as statSync3
|
|
@@ -14472,7 +14533,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
14472
14533
|
st = statSync3(path);
|
|
14473
14534
|
if (!st.isFile()) throw new Error("not a regular file");
|
|
14474
14535
|
h.update(label + "\0external-file\0" + path + "\0" + String(st.mode) + "\0");
|
|
14475
|
-
h.update(
|
|
14536
|
+
h.update(readFileSync13(path));
|
|
14476
14537
|
h.update("\0");
|
|
14477
14538
|
} catch (e) {
|
|
14478
14539
|
throw new Unattestable(
|
|
@@ -14506,7 +14567,7 @@ function fingerprintRoot(root, cwd, h) {
|
|
|
14506
14567
|
h.update(rel + "\0" + ident.kind + "\0" + String(ident.mode) + "\0");
|
|
14507
14568
|
if (ident.kind === "file") {
|
|
14508
14569
|
try {
|
|
14509
|
-
h.update(
|
|
14570
|
+
h.update(readFileSync13(path));
|
|
14510
14571
|
h.update("\0");
|
|
14511
14572
|
} catch (e) {
|
|
14512
14573
|
throw new Unattestable(
|
|
@@ -14677,7 +14738,7 @@ function venvProblem(root) {
|
|
|
14677
14738
|
const cfg = join16(root, "pyvenv.cfg");
|
|
14678
14739
|
if (!existsSync13(cfg)) return "VIRTUAL_ENV " + JSON.stringify(root) + " has no pyvenv.cfg";
|
|
14679
14740
|
try {
|
|
14680
|
-
const body =
|
|
14741
|
+
const body = readFileSync13(cfg, "utf8");
|
|
14681
14742
|
if (/^\s*include-system-site-packages\s*=\s*true\s*$/im.test(body)) {
|
|
14682
14743
|
return "Python environment " + JSON.stringify(root) + " enables system site-packages; those external package roots are not bounded";
|
|
14683
14744
|
}
|
|
@@ -15193,7 +15254,7 @@ if (child) {
|
|
|
15193
15254
|
// src/verifier-backend.ts
|
|
15194
15255
|
import { createHash as createHash11, randomUUID } from "node:crypto";
|
|
15195
15256
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
15196
|
-
import { accessSync as accessSync2, constants as constants3, readFileSync as
|
|
15257
|
+
import { accessSync as accessSync2, constants as constants3, readFileSync as readFileSync14, realpathSync as realpathSync7, statSync as statSync4 } from "node:fs";
|
|
15197
15258
|
import { delimiter as delimiter2, dirname as dirname7, join as join18, resolve as resolve8 } from "node:path";
|
|
15198
15259
|
function identity() {
|
|
15199
15260
|
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
@@ -15238,7 +15299,7 @@ function findExecutable(name) {
|
|
|
15238
15299
|
return null;
|
|
15239
15300
|
}
|
|
15240
15301
|
function fileSha256(path) {
|
|
15241
|
-
return createHash11("sha256").update(
|
|
15302
|
+
return createHash11("sha256").update(readFileSync14(path)).digest("hex");
|
|
15242
15303
|
}
|
|
15243
15304
|
function engineClientEnv() {
|
|
15244
15305
|
return {
|
|
@@ -15672,7 +15733,7 @@ var init_verifier_backend = __esm({
|
|
|
15672
15733
|
// src/verification-state.ts
|
|
15673
15734
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
15674
15735
|
import { createHash as createHash12 } from "node:crypto";
|
|
15675
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
15736
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
15676
15737
|
import { dirname as dirname8, join as join19 } from "node:path";
|
|
15677
15738
|
function git2(args, cwd) {
|
|
15678
15739
|
return execFileSync8("git", args, {
|
|
@@ -15721,7 +15782,7 @@ function interventionWiring(cwd) {
|
|
|
15721
15782
|
return sources.map(([source, path]) => {
|
|
15722
15783
|
let parsed;
|
|
15723
15784
|
try {
|
|
15724
|
-
parsed = JSON.parse(
|
|
15785
|
+
parsed = JSON.parse(readFileSync15(path, "utf8"));
|
|
15725
15786
|
} catch {
|
|
15726
15787
|
return { source, present: false };
|
|
15727
15788
|
}
|
|
@@ -15874,7 +15935,7 @@ function readVerificationRecord(cwd) {
|
|
|
15874
15935
|
if (!path) return null;
|
|
15875
15936
|
let raw;
|
|
15876
15937
|
try {
|
|
15877
|
-
raw =
|
|
15938
|
+
raw = readFileSync15(path, "utf8");
|
|
15878
15939
|
} catch {
|
|
15879
15940
|
return null;
|
|
15880
15941
|
}
|
|
@@ -15923,7 +15984,7 @@ function readVerifyingMarker(cwd) {
|
|
|
15923
15984
|
if (!path) return null;
|
|
15924
15985
|
let parsed;
|
|
15925
15986
|
try {
|
|
15926
|
-
parsed = JSON.parse(
|
|
15987
|
+
parsed = JSON.parse(readFileSync15(path, "utf8"));
|
|
15927
15988
|
} catch {
|
|
15928
15989
|
return null;
|
|
15929
15990
|
}
|
|
@@ -16035,7 +16096,7 @@ var init_verification_state = __esm({
|
|
|
16035
16096
|
// src/cli/verify.ts
|
|
16036
16097
|
import { execFileSync as execFileSync9, spawnSync as spawnSync3 } from "node:child_process";
|
|
16037
16098
|
import { createHash as createHash13 } from "node:crypto";
|
|
16038
|
-
import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readFileSync as
|
|
16099
|
+
import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync8, symlinkSync, writeFileSync as writeFileSync10, existsSync as existsSync14 } from "node:fs";
|
|
16039
16100
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
16040
16101
|
import { dirname as dirname9, isAbsolute as isAbsolute11, join as join20, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
|
|
16041
16102
|
function oracleAssuranceReport() {
|
|
@@ -16295,7 +16356,7 @@ function overlayDigest(dest, paths) {
|
|
|
16295
16356
|
try {
|
|
16296
16357
|
const st = lstatSync9(p);
|
|
16297
16358
|
h.update(String(st.mode));
|
|
16298
|
-
h.update(st.isSymbolicLink() ? readlinkSync3(p) :
|
|
16359
|
+
h.update(st.isSymbolicLink() ? readlinkSync3(p) : readFileSync16(p));
|
|
16299
16360
|
} catch {
|
|
16300
16361
|
h.update("<gone>");
|
|
16301
16362
|
}
|
|
@@ -17092,7 +17153,7 @@ var init_verify = __esm({
|
|
|
17092
17153
|
// src/cli/run.ts
|
|
17093
17154
|
import { execFileSync as execFileSync10, spawn, spawnSync as spawnSync4 } from "node:child_process";
|
|
17094
17155
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
17095
|
-
import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as
|
|
17156
|
+
import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync9, statSync as statSync5, writeFileSync as writeFileSync11 } from "node:fs";
|
|
17096
17157
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
17097
17158
|
import { dirname as dirname10, join as join21, resolve as resolve10 } from "node:path";
|
|
17098
17159
|
function authoritativeRunLifecyclePlatform(platform = process.platform) {
|
|
@@ -17246,7 +17307,7 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
|
|
|
17246
17307
|
}
|
|
17247
17308
|
let state = {};
|
|
17248
17309
|
try {
|
|
17249
|
-
state = JSON.parse(
|
|
17310
|
+
state = JSON.parse(readFileSync17(resultFile, "utf8"));
|
|
17250
17311
|
} catch {
|
|
17251
17312
|
return {
|
|
17252
17313
|
exit: 1,
|
|
@@ -17290,7 +17351,7 @@ function observerExited(pid) {
|
|
|
17290
17351
|
if (!pidAlive4(pid)) return true;
|
|
17291
17352
|
if (process.platform !== "linux") return false;
|
|
17292
17353
|
try {
|
|
17293
|
-
const stat =
|
|
17354
|
+
const stat = readFileSync17(`/proc/${pid}/stat`, "utf8");
|
|
17294
17355
|
const state = stat.slice(stat.lastIndexOf(")") + 2).split(" ")[0];
|
|
17295
17356
|
return state === "Z" || state === "X";
|
|
17296
17357
|
} catch (e) {
|
|
@@ -17412,7 +17473,7 @@ function git4(args, cwd) {
|
|
|
17412
17473
|
}
|
|
17413
17474
|
function nowTicks() {
|
|
17414
17475
|
try {
|
|
17415
|
-
return Math.floor(parseFloat(
|
|
17476
|
+
return Math.floor(parseFloat(readFileSync17("/proc/uptime", "utf8").split(" ")[0]) * 100);
|
|
17416
17477
|
} catch {
|
|
17417
17478
|
return Number.POSITIVE_INFINITY;
|
|
17418
17479
|
}
|
|
@@ -17435,7 +17496,7 @@ function survivorsHoldingTree(cwd, spawnedAfterTicks) {
|
|
|
17435
17496
|
const n2 = Number(pid);
|
|
17436
17497
|
if (n2 === process.pid) continue;
|
|
17437
17498
|
try {
|
|
17438
|
-
const stat =
|
|
17499
|
+
const stat = readFileSync17(`/proc/${pid}/stat`, "utf8");
|
|
17439
17500
|
const startTicks = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19]);
|
|
17440
17501
|
if (!Number.isFinite(startTicks) || startTicks < spawnedAfterTicks) continue;
|
|
17441
17502
|
} catch {
|
|
@@ -18008,7 +18069,7 @@ raise SystemExit(0)
|
|
|
18008
18069
|
|
|
18009
18070
|
// src/cli/doctor.ts
|
|
18010
18071
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
18011
|
-
import { existsSync as existsSync15, readFileSync as
|
|
18072
|
+
import { existsSync as existsSync15, readFileSync as readFileSync18, readdirSync as readdirSync5 } from "node:fs";
|
|
18012
18073
|
import { join as join22, resolve as resolve11 } from "node:path";
|
|
18013
18074
|
function lifecyclePlatformCheck(platform = process.platform, linuxPython = platform === "linux" ? trustedLinuxPython() : null) {
|
|
18014
18075
|
if (platform === "linux") {
|
|
@@ -18067,7 +18128,7 @@ function pinsInFile(path) {
|
|
|
18067
18128
|
if (!existsSync15(path)) return [];
|
|
18068
18129
|
let src = "";
|
|
18069
18130
|
try {
|
|
18070
|
-
src =
|
|
18131
|
+
src = readFileSync18(path, "utf8");
|
|
18071
18132
|
} catch {
|
|
18072
18133
|
return [];
|
|
18073
18134
|
}
|
|
@@ -18094,7 +18155,7 @@ function workflowPermissionCheck(cwd, workflowRels = [".github/workflows/tamperw
|
|
|
18094
18155
|
}
|
|
18095
18156
|
let doc;
|
|
18096
18157
|
try {
|
|
18097
|
-
doc = yaml.parse(
|
|
18158
|
+
doc = yaml.parse(readFileSync18(path, "utf8"));
|
|
18098
18159
|
} catch (e) {
|
|
18099
18160
|
broken.push(`${rel} is not valid YAML (${e instanceof Error ? e.message : String(e)})`);
|
|
18100
18161
|
continue;
|
|
@@ -18518,7 +18579,7 @@ function diagnose(opts = {}) {
|
|
|
18518
18579
|
}
|
|
18519
18580
|
let doc;
|
|
18520
18581
|
try {
|
|
18521
|
-
doc = yaml.parse(
|
|
18582
|
+
doc = yaml.parse(readFileSync18(workflowPath, "utf8"));
|
|
18522
18583
|
} catch (e) {
|
|
18523
18584
|
return fail(
|
|
18524
18585
|
"ci-verifier",
|
|
@@ -18831,7 +18892,7 @@ import { execFileSync as execFileSync12, spawnSync as spawnSync5 } from "node:ch
|
|
|
18831
18892
|
import {
|
|
18832
18893
|
existsSync as existsSync16,
|
|
18833
18894
|
mkdtempSync as mkdtempSync5,
|
|
18834
|
-
readFileSync as
|
|
18895
|
+
readFileSync as readFileSync19,
|
|
18835
18896
|
readdirSync as readdirSync6,
|
|
18836
18897
|
realpathSync as realpathSync9,
|
|
18837
18898
|
rmSync as rmSync10,
|
|
@@ -19145,7 +19206,7 @@ function straceOnce(root, command, budget) {
|
|
|
19145
19206
|
}
|
|
19146
19207
|
);
|
|
19147
19208
|
const logs = traced.error ? [] : traceFiles(prefix);
|
|
19148
|
-
const accesses = logs.flatMap((path) => parseStraceFileAccess(
|
|
19209
|
+
const accesses = logs.flatMap((path) => parseStraceFileAccess(readFileSync19(path, "utf8"), TRACE_ROOT));
|
|
19149
19210
|
const raw = {
|
|
19150
19211
|
spawnError: traced.error ? traced.error.message : null,
|
|
19151
19212
|
status: traced.status,
|
|
@@ -19377,7 +19438,7 @@ var init_trace_verify = __esm({
|
|
|
19377
19438
|
});
|
|
19378
19439
|
|
|
19379
19440
|
// src/runtimes.ts
|
|
19380
|
-
import { existsSync as existsSync17, readFileSync as
|
|
19441
|
+
import { existsSync as existsSync17, readFileSync as readFileSync20 } from "node:fs";
|
|
19381
19442
|
import { join as join24 } from "node:path";
|
|
19382
19443
|
function isRecord5(x) {
|
|
19383
19444
|
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
@@ -19408,7 +19469,7 @@ function claudeSettingsBeyondTamperward(cwd) {
|
|
|
19408
19469
|
let raw;
|
|
19409
19470
|
try {
|
|
19410
19471
|
if (!existsSync17(path)) return false;
|
|
19411
|
-
raw =
|
|
19472
|
+
raw = readFileSync20(path, "utf8");
|
|
19412
19473
|
} catch {
|
|
19413
19474
|
return false;
|
|
19414
19475
|
}
|
|
@@ -19515,7 +19576,7 @@ var init_runtimes = __esm({
|
|
|
19515
19576
|
|
|
19516
19577
|
// src/cli/onboard.ts
|
|
19517
19578
|
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
19518
|
-
import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as
|
|
19579
|
+
import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync21, realpathSync as realpathSync10, rmSync as rmSync11 } from "node:fs";
|
|
19519
19580
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
19520
19581
|
import { join as join25, relative as relative8, resolve as resolve13, sep as sep6 } from "node:path";
|
|
19521
19582
|
import { createInterface } from "node:readline";
|
|
@@ -19588,7 +19649,7 @@ function writeVerifyCommand(cwd, command) {
|
|
|
19588
19649
|
return `${POLICY_FILE} is not a regular file; refusing to follow or replace a symlink or special file`;
|
|
19589
19650
|
}
|
|
19590
19651
|
const mode = existingMode(path, 420);
|
|
19591
|
-
const original = kind === "file" ?
|
|
19652
|
+
const original = kind === "file" ? readFileSync21(path, "utf8") : null;
|
|
19592
19653
|
let next;
|
|
19593
19654
|
try {
|
|
19594
19655
|
const doc = parseDocument(original && original.trim() ? original : "version: 1\n");
|
|
@@ -20200,7 +20261,7 @@ var init_capture = __esm({
|
|
|
20200
20261
|
|
|
20201
20262
|
// src/research/manifest.ts
|
|
20202
20263
|
import { createHash as createHash14 } from "node:crypto";
|
|
20203
|
-
import { readFileSync as
|
|
20264
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
20204
20265
|
import { dirname as dirname11, resolve as resolve15 } from "node:path";
|
|
20205
20266
|
function taskFrom(raw, index, manifestDir) {
|
|
20206
20267
|
const where = `task #${index + 1}`;
|
|
@@ -20234,7 +20295,7 @@ function readManifest(path) {
|
|
|
20234
20295
|
const abs = resolve15(path);
|
|
20235
20296
|
let bytes;
|
|
20236
20297
|
try {
|
|
20237
|
-
bytes =
|
|
20298
|
+
bytes = readFileSync22(abs);
|
|
20238
20299
|
} catch (e) {
|
|
20239
20300
|
throw new ResearchError(`cannot read task manifest ${abs}: ${e instanceof Error ? e.message : String(e)}`);
|
|
20240
20301
|
}
|
|
@@ -20516,7 +20577,52 @@ function pairRecordFrom(raw, where = "record") {
|
|
|
20516
20577
|
}
|
|
20517
20578
|
};
|
|
20518
20579
|
}
|
|
20519
|
-
|
|
20580
|
+
function onlyFields(raw, allowed, where) {
|
|
20581
|
+
if (!isRecord(raw)) return;
|
|
20582
|
+
for (const key2 of Object.keys(raw)) {
|
|
20583
|
+
if (!allowed.includes(key2)) bad(`${where}.${key2} is not a field of the v1 pair record; refusing to carry it as portable evidence`);
|
|
20584
|
+
}
|
|
20585
|
+
}
|
|
20586
|
+
function portableObject(raw, spec2, where) {
|
|
20587
|
+
if (!isRecord(raw)) bad(`${where} is not an object`);
|
|
20588
|
+
for (const [key2, value] of Object.entries(raw)) {
|
|
20589
|
+
const field2 = spec2.get(key2);
|
|
20590
|
+
const at = `${where}.${key2}`;
|
|
20591
|
+
if (field2 === void 0) bad(`${at} is not a field of the v1 run document; refusing to carry it as portable evidence`);
|
|
20592
|
+
if (field2 === "scalar") {
|
|
20593
|
+
if (value !== null && typeof value === "object") bad(`${at} is not a scalar; refusing to carry it as portable evidence`);
|
|
20594
|
+
} else if ("items" in field2) {
|
|
20595
|
+
if (!Array.isArray(value)) bad(`${at} is not an array`);
|
|
20596
|
+
value.forEach((item, i) => portableObject(item, field2.items, `${at}[${i}]`));
|
|
20597
|
+
} else {
|
|
20598
|
+
portableObject(value, field2.fields, at);
|
|
20599
|
+
}
|
|
20600
|
+
}
|
|
20601
|
+
}
|
|
20602
|
+
function portablePairRecordFrom(raw, where = "record") {
|
|
20603
|
+
onlyFields(raw, PAIR_FIELDS, where);
|
|
20604
|
+
if (isRecord(raw)) {
|
|
20605
|
+
onlyFields(raw.adapter, ADAPTER_FIELDS, `${where}.adapter`);
|
|
20606
|
+
onlyFields(raw.arms, ARMS_FIELDS, `${where}.arms`);
|
|
20607
|
+
if (isRecord(raw.arms)) {
|
|
20608
|
+
for (const arm of ARMS_FIELDS) {
|
|
20609
|
+
const trajectory = raw.arms[arm];
|
|
20610
|
+
const at = `${where}.arms.${arm}`;
|
|
20611
|
+
onlyFields(trajectory, TRAJECTORY_FIELDS, at);
|
|
20612
|
+
if (isRecord(trajectory)) {
|
|
20613
|
+
onlyFields(trajectory.agent, AGENT_FIELDS, `${at}.agent`);
|
|
20614
|
+
onlyFields(trajectory.treatment, TREATMENT_FIELDS, `${at}.treatment`);
|
|
20615
|
+
if (isRecord(trajectory.treatment) && trajectory.treatment.envelope !== null && trajectory.treatment.envelope !== void 0) {
|
|
20616
|
+
portableObject(trajectory.treatment.envelope, RUN_DOCUMENT_FIELDS, `${at}.treatment.envelope`);
|
|
20617
|
+
}
|
|
20618
|
+
onlyFields(trajectory.outcome, OUTCOME_FIELDS, `${at}.outcome`);
|
|
20619
|
+
}
|
|
20620
|
+
}
|
|
20621
|
+
}
|
|
20622
|
+
}
|
|
20623
|
+
return pairRecordFrom(raw, where);
|
|
20624
|
+
}
|
|
20625
|
+
var SHA_RE, SHA256_RE, PAIR_FIELDS, ADAPTER_FIELDS, ARMS_FIELDS, TRAJECTORY_FIELDS, AGENT_FIELDS, TREATMENT_FIELDS, OUTCOME_FIELDS, fields, items, RUN_DOCUMENT_FIELDS;
|
|
20520
20626
|
var init_record = __esm({
|
|
20521
20627
|
"src/research/record.ts"() {
|
|
20522
20628
|
"use strict";
|
|
@@ -20526,13 +20632,89 @@ var init_record = __esm({
|
|
|
20526
20632
|
init_derive();
|
|
20527
20633
|
SHA_RE = /^[0-9a-f]{40,64}$/;
|
|
20528
20634
|
SHA256_RE = /^[0-9a-f]{64}$/;
|
|
20635
|
+
PAIR_FIELDS = [
|
|
20636
|
+
"schema_version",
|
|
20637
|
+
"command",
|
|
20638
|
+
"document",
|
|
20639
|
+
"task",
|
|
20640
|
+
"pair",
|
|
20641
|
+
"adapter",
|
|
20642
|
+
"model",
|
|
20643
|
+
"tamperward_version",
|
|
20644
|
+
"agent_argv",
|
|
20645
|
+
"agent_budget",
|
|
20646
|
+
"manifest_sha256",
|
|
20647
|
+
"verify_command",
|
|
20648
|
+
"arms"
|
|
20649
|
+
];
|
|
20650
|
+
ADAPTER_FIELDS = ["name", "layers"];
|
|
20651
|
+
ARMS_FIELDS = ["ungated", "gated"];
|
|
20652
|
+
TRAJECTORY_FIELDS = [
|
|
20653
|
+
"arm",
|
|
20654
|
+
"workspace",
|
|
20655
|
+
"base",
|
|
20656
|
+
"head",
|
|
20657
|
+
"started_at",
|
|
20658
|
+
"finished_at",
|
|
20659
|
+
"agent",
|
|
20660
|
+
"treatment",
|
|
20661
|
+
"outcome",
|
|
20662
|
+
"released_green",
|
|
20663
|
+
"measured",
|
|
20664
|
+
"unmeasurable"
|
|
20665
|
+
];
|
|
20666
|
+
AGENT_FIELDS = ["exit_code", "signal", "timed_out", "failure"];
|
|
20667
|
+
TREATMENT_FIELDS = ["verdict", "exit_code", "complete", "disposition", "envelope"];
|
|
20668
|
+
OUTCOME_FIELDS = [
|
|
20669
|
+
"verify_verdict",
|
|
20670
|
+
"visible_exit",
|
|
20671
|
+
"pristine_exit",
|
|
20672
|
+
"visible_green",
|
|
20673
|
+
"pristine_green",
|
|
20674
|
+
"masked_failure",
|
|
20675
|
+
"surviving_protected_mutations",
|
|
20676
|
+
"warn_findings",
|
|
20677
|
+
"rules",
|
|
20678
|
+
"honest_completion"
|
|
20679
|
+
];
|
|
20680
|
+
fields = (entries) => ({ fields: new Map(entries) });
|
|
20681
|
+
items = (entries) => ({ items: new Map(entries) });
|
|
20682
|
+
RUN_DOCUMENT_FIELDS = /* @__PURE__ */ new Map([
|
|
20683
|
+
["schema_version", "scalar"],
|
|
20684
|
+
["verdict", "scalar"],
|
|
20685
|
+
["exit_code", "scalar"],
|
|
20686
|
+
["complete", "scalar"],
|
|
20687
|
+
["base", "scalar"],
|
|
20688
|
+
["head", "scalar"],
|
|
20689
|
+
["reason", "scalar"],
|
|
20690
|
+
["agent", fields([["exit_code", "scalar"], ["timed_out", "scalar"], ["lifecycle_owned", "scalar"], ["budget_secs", "scalar"]])],
|
|
20691
|
+
["checks", fields([["diff", "scalar"], ["worktree", "scalar"], ["verify", "scalar"]])],
|
|
20692
|
+
["verifier_backend", fields([
|
|
20693
|
+
["kind", "scalar"],
|
|
20694
|
+
["trust", "scalar"],
|
|
20695
|
+
["available", "scalar"],
|
|
20696
|
+
["image", "scalar"],
|
|
20697
|
+
["engine", "scalar"],
|
|
20698
|
+
["reason", "scalar"],
|
|
20699
|
+
["resources", fields([["memory_bytes", "scalar"], ["memory_swap_bytes", "scalar"], ["cpus", "scalar"], ["pids", "scalar"]])]
|
|
20700
|
+
])],
|
|
20701
|
+
["dependency_environment", fields([
|
|
20702
|
+
["status", "scalar"],
|
|
20703
|
+
["fingerprint", "scalar"],
|
|
20704
|
+
["reason", "scalar"],
|
|
20705
|
+
["image", "scalar"],
|
|
20706
|
+
["roots", items([["kind", "scalar"], ["path", "scalar"]])],
|
|
20707
|
+
["diagnostics", fields([["full_snapshots", "scalar"], ["reused_snapshots", "scalar"], ["total_ms", "scalar"]])]
|
|
20708
|
+
])],
|
|
20709
|
+
["observer", fields([["enabled", "scalar"], ["blocking", "scalar"]])]
|
|
20710
|
+
]);
|
|
20529
20711
|
}
|
|
20530
20712
|
});
|
|
20531
20713
|
|
|
20532
20714
|
// src/research/run.ts
|
|
20533
20715
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
20534
20716
|
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
20535
|
-
import { closeSync as
|
|
20717
|
+
import { closeSync as closeSync4, existsSync as existsSync18, linkSync, mkdirSync as mkdirSync10, openSync as openSync4, readFileSync as readFileSync23, renameSync as renameSync4, rmSync as rmSync12, unlinkSync, writeFileSync as writeFileSync12, writeSync } from "node:fs";
|
|
20536
20718
|
import { hostname } from "node:os";
|
|
20537
20719
|
import { join as join26, resolve as resolve16 } from "node:path";
|
|
20538
20720
|
function git7(args, cwd) {
|
|
@@ -20546,7 +20728,7 @@ function researchLockPath(ledger) {
|
|
|
20546
20728
|
}
|
|
20547
20729
|
function readLockOwner(path) {
|
|
20548
20730
|
try {
|
|
20549
|
-
const parsed = JSON.parse(
|
|
20731
|
+
const parsed = JSON.parse(readFileSync23(path, "utf8"));
|
|
20550
20732
|
if (!isRecord(parsed) || typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
|
|
20551
20733
|
if (typeof parsed.host !== "string" || typeof parsed.started_at !== "string" || typeof parsed.token !== "string") return null;
|
|
20552
20734
|
return { pid: parsed.pid, host: parsed.host, started_at: parsed.started_at, token: parsed.token };
|
|
@@ -20612,12 +20794,12 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
|
20612
20794
|
const text = JSON.stringify(owner) + "\n";
|
|
20613
20795
|
let fd = -1;
|
|
20614
20796
|
try {
|
|
20615
|
-
fd =
|
|
20797
|
+
fd = openSync4(path, "wx", 384);
|
|
20616
20798
|
writeSync(fd, text);
|
|
20617
|
-
|
|
20799
|
+
closeSync4(fd);
|
|
20618
20800
|
fd = -1;
|
|
20619
20801
|
} catch (e) {
|
|
20620
|
-
if (fd !== -1)
|
|
20802
|
+
if (fd !== -1) closeSync4(fd);
|
|
20621
20803
|
if (e.code === "EEXIST") {
|
|
20622
20804
|
throw new ResearchError(
|
|
20623
20805
|
`research output ${ledger} is already locked by ${lockHolder(path)}; refusing concurrent writers. Verify the owner is not running, then remove ${path} deliberately or rerun with --break-lock`
|
|
@@ -20631,7 +20813,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
|
20631
20813
|
if (released) return;
|
|
20632
20814
|
released = true;
|
|
20633
20815
|
try {
|
|
20634
|
-
if (
|
|
20816
|
+
if (readFileSync23(path, "utf8") === text) unlinkSync(path);
|
|
20635
20817
|
} catch {
|
|
20636
20818
|
}
|
|
20637
20819
|
}
|
|
@@ -20640,7 +20822,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
|
20640
20822
|
function resumableRecord(path, expected) {
|
|
20641
20823
|
let raw;
|
|
20642
20824
|
try {
|
|
20643
|
-
raw = JSON.parse(
|
|
20825
|
+
raw = JSON.parse(readFileSync23(path, "utf8"));
|
|
20644
20826
|
} catch (e) {
|
|
20645
20827
|
throw new ResearchError(`ledger record ${path} is malformed (${errorMessage(e).split("\n")[0]}); remove it to regenerate the pair, or use a new --out`);
|
|
20646
20828
|
}
|
|
@@ -21061,27 +21243,95 @@ var init_run2 = __esm({
|
|
|
21061
21243
|
}
|
|
21062
21244
|
});
|
|
21063
21245
|
|
|
21064
|
-
// src/research/
|
|
21065
|
-
import { readdirSync as readdirSync7,
|
|
21246
|
+
// src/research/ledger-files.ts
|
|
21247
|
+
import { closeSync as closeSync5, constants as constants4, fstatSync as fstatSync2, lstatSync as lstatSync11, openSync as openSync5, readFileSync as readFileSync24, readdirSync as readdirSync7, realpathSync as realpathSync11 } from "node:fs";
|
|
21066
21248
|
import { join as join27, resolve as resolve17 } from "node:path";
|
|
21067
|
-
function
|
|
21068
|
-
|
|
21249
|
+
function lstatOrNull(path) {
|
|
21250
|
+
try {
|
|
21251
|
+
return lstatSync11(path);
|
|
21252
|
+
} catch {
|
|
21253
|
+
return null;
|
|
21254
|
+
}
|
|
21255
|
+
}
|
|
21256
|
+
function kindOf(st) {
|
|
21257
|
+
if (st.isSymbolicLink()) return "a symlink";
|
|
21258
|
+
if (st.isDirectory()) return "a directory";
|
|
21259
|
+
if (st.isFIFO()) return "a FIFO";
|
|
21260
|
+
if (st.isSocket()) return "a socket";
|
|
21261
|
+
if (st.isCharacterDevice() || st.isBlockDevice()) return "a device";
|
|
21262
|
+
return "not a regular file";
|
|
21263
|
+
}
|
|
21264
|
+
function readRegularFile(path, realPairsDir, name) {
|
|
21265
|
+
const listed = lstatOrNull(path);
|
|
21266
|
+
if (listed === null) throw new ResearchError(`ledger record ${path} vanished while the ledger was being read`);
|
|
21267
|
+
if (!listed.isFile()) {
|
|
21268
|
+
throw new ResearchError(`ledger record ${path} is ${kindOf(listed)}; only a regular file placed directly in pairs/ is read`);
|
|
21269
|
+
}
|
|
21270
|
+
if (listed.nlink !== 1) {
|
|
21271
|
+
throw new ResearchError(`ledger record ${path} has ${listed.nlink} links; a hard link can name a file outside pairs/, so only a singly linked regular file is read`);
|
|
21272
|
+
}
|
|
21273
|
+
const real = realpathSync11.native(path);
|
|
21274
|
+
if (real !== join27(realPairsDir, name)) {
|
|
21275
|
+
throw new ResearchError(`ledger record ${path} resolves outside the pairs directory (${real}); refusing to read it`);
|
|
21276
|
+
}
|
|
21277
|
+
const flags = constants4.O_RDONLY | (constants4.O_NOFOLLOW ?? 0);
|
|
21278
|
+
let fd;
|
|
21279
|
+
try {
|
|
21280
|
+
fd = openSync5(path, flags);
|
|
21281
|
+
} catch (e) {
|
|
21282
|
+
throw new ResearchError(`cannot open ledger record ${path}: ${errorMessage(e)}`);
|
|
21283
|
+
}
|
|
21284
|
+
try {
|
|
21285
|
+
const opened = fstatSync2(fd);
|
|
21286
|
+
const sameInode = process.platform === "win32" || opened.dev === listed.dev && opened.ino === listed.ino;
|
|
21287
|
+
if (!opened.isFile() || !sameInode || opened.nlink !== 1) {
|
|
21288
|
+
throw new ResearchError(`ledger record ${path} changed between listing and reading; refusing to read it`);
|
|
21289
|
+
}
|
|
21290
|
+
return readFileSync24(fd);
|
|
21291
|
+
} finally {
|
|
21292
|
+
closeSync5(fd);
|
|
21293
|
+
}
|
|
21294
|
+
}
|
|
21295
|
+
function readPairFiles(ledger) {
|
|
21296
|
+
const root = resolve17(ledger);
|
|
21297
|
+
const pairsDir = join27(root, "pairs");
|
|
21298
|
+
const dirStat = lstatOrNull(pairsDir);
|
|
21299
|
+
if (dirStat !== null && dirStat.isSymbolicLink()) {
|
|
21300
|
+
throw new ResearchError(`ledger pairs directory ${pairsDir} is a symlink; only a real directory is read`);
|
|
21301
|
+
}
|
|
21302
|
+
if (dirStat === null || !dirStat.isDirectory()) throw new ResearchError(`ledger ${root} has no pairs directory`);
|
|
21303
|
+
const realPairsDir = realpathSync11.native(pairsDir);
|
|
21069
21304
|
let names;
|
|
21070
21305
|
try {
|
|
21071
21306
|
names = readdirSync7(pairsDir).filter((n2) => n2.endsWith(".json")).sort();
|
|
21072
21307
|
} catch (e) {
|
|
21073
21308
|
throw new ResearchError(`cannot read ledger ${pairsDir}: ${errorMessage(e)}`);
|
|
21074
21309
|
}
|
|
21075
|
-
|
|
21076
|
-
|
|
21077
|
-
|
|
21310
|
+
return names.map((name) => {
|
|
21311
|
+
const path = join27(pairsDir, name);
|
|
21312
|
+
return { name, path, bytes: readRegularFile(path, realPairsDir, name) };
|
|
21313
|
+
});
|
|
21314
|
+
}
|
|
21315
|
+
var init_ledger_files = __esm({
|
|
21316
|
+
"src/research/ledger-files.ts"() {
|
|
21317
|
+
"use strict";
|
|
21318
|
+
init_narrow();
|
|
21319
|
+
init_adapter();
|
|
21320
|
+
}
|
|
21321
|
+
});
|
|
21322
|
+
|
|
21323
|
+
// src/research/summarize.ts
|
|
21324
|
+
function readLedger2(dir) {
|
|
21325
|
+
const files = readPairFiles(dir);
|
|
21326
|
+
if (files.length === 0) throw new ResearchError(`ledger ${dir} holds no pair records`);
|
|
21327
|
+
return files.map((f) => {
|
|
21078
21328
|
let raw;
|
|
21079
21329
|
try {
|
|
21080
|
-
raw = JSON.parse(
|
|
21330
|
+
raw = JSON.parse(f.bytes.toString("utf8"));
|
|
21081
21331
|
} catch (e) {
|
|
21082
|
-
throw new ResearchError(`ledger record ${path} is not valid JSON: ${errorMessage(e)}`);
|
|
21332
|
+
throw new ResearchError(`ledger record ${f.path} is not valid JSON: ${errorMessage(e)}`);
|
|
21083
21333
|
}
|
|
21084
|
-
return pairRecordFrom(raw, path);
|
|
21334
|
+
return pairRecordFrom(raw, f.path);
|
|
21085
21335
|
});
|
|
21086
21336
|
}
|
|
21087
21337
|
function weakened(t) {
|
|
@@ -21202,6 +21452,7 @@ var init_summarize = __esm({
|
|
|
21202
21452
|
"use strict";
|
|
21203
21453
|
init_machine_output();
|
|
21204
21454
|
init_narrow();
|
|
21455
|
+
init_ledger_files();
|
|
21205
21456
|
init_adapter();
|
|
21206
21457
|
init_record();
|
|
21207
21458
|
}
|
|
@@ -21209,7 +21460,7 @@ var init_summarize = __esm({
|
|
|
21209
21460
|
|
|
21210
21461
|
// src/research/init.ts
|
|
21211
21462
|
import { createHash as createHash15 } from "node:crypto";
|
|
21212
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as
|
|
21463
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync25, writeFileSync as writeFileSync13 } from "node:fs";
|
|
21213
21464
|
import { basename as basename5, dirname as dirname12, resolve as resolve18 } from "node:path";
|
|
21214
21465
|
function idFrom(repo) {
|
|
21215
21466
|
const clean = repo.replace(/[\\/]$/, "").split(/[\\/]/).pop() || "task";
|
|
@@ -21261,7 +21512,7 @@ function runResearchInit(opts) {
|
|
|
21261
21512
|
}
|
|
21262
21513
|
}
|
|
21263
21514
|
function requireHash(path) {
|
|
21264
|
-
return createHash15("sha256").update(
|
|
21515
|
+
return createHash15("sha256").update(readFileSync25(path)).digest("hex");
|
|
21265
21516
|
}
|
|
21266
21517
|
var init_init2 = __esm({
|
|
21267
21518
|
"src/research/init.ts"() {
|
|
@@ -21328,63 +21579,105 @@ var init_report2 = __esm({
|
|
|
21328
21579
|
// src/research/bundle.ts
|
|
21329
21580
|
import { createHash as createHash16 } from "node:crypto";
|
|
21330
21581
|
import { gzipSync, gunzipSync } from "node:zlib";
|
|
21331
|
-
import {
|
|
21332
|
-
import { dirname as dirname13,
|
|
21582
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync26, writeFileSync as writeFileSync14 } from "node:fs";
|
|
21583
|
+
import { dirname as dirname13, resolve as resolve19 } from "node:path";
|
|
21333
21584
|
function octal(value, width) {
|
|
21334
21585
|
return value.toString(8).padStart(width - 1, "0") + "\0";
|
|
21335
21586
|
}
|
|
21336
|
-
function
|
|
21587
|
+
function tarHeader(name, size) {
|
|
21337
21588
|
const header = Buffer.alloc(BLOCK, 0);
|
|
21338
|
-
|
|
21339
|
-
header.write(safe, 0, "utf8");
|
|
21589
|
+
header.write(name, 0, "utf8");
|
|
21340
21590
|
header.write(octal(420, 8), 100, "ascii");
|
|
21341
21591
|
header.write(octal(0, 8), 108, "ascii");
|
|
21342
21592
|
header.write(octal(0, 8), 116, "ascii");
|
|
21343
|
-
header.write(octal(
|
|
21593
|
+
header.write(octal(size, 12), 124, "ascii");
|
|
21344
21594
|
header.write(octal(0, 12), 136, "ascii");
|
|
21345
21595
|
header.fill(32, 148, 156);
|
|
21346
|
-
header[
|
|
21596
|
+
header[TYPE_OFFSET] = 48;
|
|
21347
21597
|
header.write("ustar", 257, "ascii");
|
|
21348
21598
|
header.write("00", 263, "ascii");
|
|
21349
21599
|
const checksum = header.reduce((sum, b) => sum + b, 0);
|
|
21350
21600
|
header.write(octal(checksum, 8), 148, "ascii");
|
|
21601
|
+
return header;
|
|
21602
|
+
}
|
|
21603
|
+
function tarEntry(name, bytes) {
|
|
21604
|
+
if (name.startsWith("/") || Buffer.byteLength(name, "utf8") > TAR_NAME_MAX) {
|
|
21605
|
+
throw new ResearchError(`archive entry name ${name} cannot be stored byte for byte in a ustar header`);
|
|
21606
|
+
}
|
|
21351
21607
|
const padding = Buffer.alloc((BLOCK - bytes.length % BLOCK) % BLOCK, 0);
|
|
21352
|
-
return Buffer.concat([
|
|
21608
|
+
return Buffer.concat([tarHeader(name, bytes.length), bytes, padding]);
|
|
21353
21609
|
}
|
|
21354
21610
|
function makeTar(entries) {
|
|
21355
21611
|
return Buffer.concat([...entries.map((e) => tarEntry(e.name, e.bytes)), Buffer.alloc(BLOCK * 2, 0)]);
|
|
21356
21612
|
}
|
|
21613
|
+
function field(header, start, length) {
|
|
21614
|
+
const raw = header.subarray(start, start + length);
|
|
21615
|
+
const end = raw.indexOf(0);
|
|
21616
|
+
return raw.subarray(0, end === -1 ? raw.length : end).toString("utf8");
|
|
21617
|
+
}
|
|
21357
21618
|
function parseTar(bytes) {
|
|
21358
21619
|
const files = /* @__PURE__ */ new Map();
|
|
21359
|
-
|
|
21620
|
+
let offset = 0;
|
|
21621
|
+
for (; ; ) {
|
|
21622
|
+
if (offset + BLOCK > bytes.length) throw new ResearchError("research bundle is missing its end-of-archive blocks");
|
|
21360
21623
|
const header = bytes.subarray(offset, offset + BLOCK);
|
|
21361
|
-
if (header.every((b) => b === 0))
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21624
|
+
if (header.every((b) => b === 0)) {
|
|
21625
|
+
const second = bytes.subarray(offset + BLOCK, offset + 2 * BLOCK);
|
|
21626
|
+
if (second.length !== BLOCK || !second.every((b) => b === 0) || offset + 2 * BLOCK !== bytes.length) {
|
|
21627
|
+
throw new ResearchError("research bundle carries bytes after its end-of-archive blocks");
|
|
21628
|
+
}
|
|
21629
|
+
return files;
|
|
21630
|
+
}
|
|
21631
|
+
const name = field(header, 0, NAME_FIELD);
|
|
21632
|
+
const prefix = field(header, PREFIX_OFFSET, PREFIX_FIELD);
|
|
21633
|
+
if (prefix) {
|
|
21634
|
+
throw new ResearchError(`research bundle entry ${prefix}/${name} uses a ustar prefix; this bundle format stores every name in the name field`);
|
|
21635
|
+
}
|
|
21636
|
+
const type = header[TYPE_OFFSET];
|
|
21637
|
+
if (type !== 48) {
|
|
21638
|
+
throw new ResearchError(`research bundle entry ${name} is not a regular file entry (type flag ${JSON.stringify(String.fromCharCode(type))})`);
|
|
21639
|
+
}
|
|
21640
|
+
const size = Number.parseInt(field(header, 124, 12).trim() || "0", 8);
|
|
21365
21641
|
if (!name || name.startsWith("/") || name.split("/").includes("..") || !Number.isSafeInteger(size) || size < 0) {
|
|
21366
21642
|
throw new ResearchError("research bundle contains an unsafe or malformed archive entry");
|
|
21367
21643
|
}
|
|
21644
|
+
if (Buffer.byteLength(name, "utf8") > TAR_NAME_MAX || !header.equals(tarHeader(name, size))) {
|
|
21645
|
+
throw new ResearchError(`research bundle entry ${name} has a header this bundle format does not write`);
|
|
21646
|
+
}
|
|
21368
21647
|
const start = offset + BLOCK;
|
|
21369
21648
|
const end = start + size;
|
|
21370
21649
|
if (end > bytes.length) throw new ResearchError("research bundle is truncated");
|
|
21650
|
+
const padded = start + Math.ceil(size / BLOCK) * BLOCK;
|
|
21651
|
+
if (padded > bytes.length || !bytes.subarray(end, padded).every((b) => b === 0)) {
|
|
21652
|
+
throw new ResearchError(`research bundle entry ${name} carries bytes in its padding`);
|
|
21653
|
+
}
|
|
21654
|
+
if (files.has(name)) throw new ResearchError(`research bundle contains a duplicate archive entry ${name}`);
|
|
21371
21655
|
files.set(name, Buffer.from(bytes.subarray(start, end)));
|
|
21372
|
-
offset =
|
|
21656
|
+
offset = padded;
|
|
21373
21657
|
}
|
|
21374
|
-
return files;
|
|
21375
21658
|
}
|
|
21376
|
-
function
|
|
21659
|
+
function packagedRecords(ledger) {
|
|
21660
|
+
const files = readPairFiles(ledger);
|
|
21661
|
+
if (files.length === 0) throw new ResearchError(`ledger ${ledger} holds no pair records`);
|
|
21662
|
+
const records = [];
|
|
21377
21663
|
const entries = [];
|
|
21378
|
-
const
|
|
21379
|
-
|
|
21380
|
-
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
|
|
21664
|
+
for (const f of files) {
|
|
21665
|
+
let raw;
|
|
21666
|
+
try {
|
|
21667
|
+
raw = JSON.parse(f.bytes.toString("utf8"));
|
|
21668
|
+
} catch (e) {
|
|
21669
|
+
throw new ResearchError(`ledger record ${f.path} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
21670
|
+
}
|
|
21671
|
+
const record = portablePairRecordFrom(raw, f.path);
|
|
21672
|
+
const archiveName = `ledger/pairs/${f.name}`;
|
|
21673
|
+
const nameBytes = Buffer.byteLength(archiveName, "utf8");
|
|
21674
|
+
if (nameBytes > TAR_NAME_MAX) {
|
|
21675
|
+
throw new ResearchError(`pair record name ${f.name} is too long for the archive (${nameBytes} bytes as ${archiveName}; the limit is ${TAR_NAME_MAX})`);
|
|
21676
|
+
}
|
|
21677
|
+
records.push(record);
|
|
21678
|
+
entries.push({ name: archiveName, bytes: jsonBytes(record) });
|
|
21386
21679
|
}
|
|
21387
|
-
return entries;
|
|
21680
|
+
return { records, entries };
|
|
21388
21681
|
}
|
|
21389
21682
|
function jsonBytes(value) {
|
|
21390
21683
|
return Buffer.from(JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
@@ -21392,10 +21685,15 @@ function jsonBytes(value) {
|
|
|
21392
21685
|
function validateResearchBundle(path) {
|
|
21393
21686
|
let files;
|
|
21394
21687
|
try {
|
|
21395
|
-
files = parseTar(gunzipSync(
|
|
21688
|
+
files = parseTar(gunzipSync(readFileSync26(resolve19(path))));
|
|
21396
21689
|
} catch (e) {
|
|
21397
21690
|
throw new ResearchError(`cannot read research bundle ${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
21398
21691
|
}
|
|
21692
|
+
for (const name of files.keys()) {
|
|
21693
|
+
if (!TOP_LEVEL_ENTRIES.has(name) && !PAIR_ENTRY_RE.test(name)) {
|
|
21694
|
+
throw new ResearchError(`research bundle carries an entry that is not evidence: ${name}`);
|
|
21695
|
+
}
|
|
21696
|
+
}
|
|
21399
21697
|
const provenance = files.get("provenance.json");
|
|
21400
21698
|
const summaryBytes = files.get("summary.json");
|
|
21401
21699
|
const report2 = files.get("report.txt");
|
|
@@ -21422,14 +21720,16 @@ function validateResearchBundle(path) {
|
|
|
21422
21720
|
if (parsedSummary.document !== "summary" || parsedSummary.command !== "research") throw new ResearchError("research bundle summary is not a research summary");
|
|
21423
21721
|
const records = [];
|
|
21424
21722
|
for (const [name, bytes] of files) {
|
|
21425
|
-
if (!
|
|
21723
|
+
if (!PAIR_ENTRY_RE.test(name)) continue;
|
|
21426
21724
|
let raw;
|
|
21427
21725
|
try {
|
|
21428
21726
|
raw = JSON.parse(bytes.toString("utf8"));
|
|
21429
21727
|
} catch {
|
|
21430
21728
|
throw new ResearchError(`invalid JSON in ${name}`);
|
|
21431
21729
|
}
|
|
21432
|
-
|
|
21730
|
+
const record = portablePairRecordFrom(raw, name);
|
|
21731
|
+
if (!bytes.equals(jsonBytes(record))) throw new ResearchError(`${name} is not the canonical serialization of its pair record`);
|
|
21732
|
+
records.push(record);
|
|
21433
21733
|
}
|
|
21434
21734
|
if (records.length === 0) throw new ResearchError("research bundle contains no pair records");
|
|
21435
21735
|
const derived = summarizeRecords(records);
|
|
@@ -21443,10 +21743,10 @@ function createResearchBundle(opts) {
|
|
|
21443
21743
|
if (!opts.ledger) throw new ResearchError("research bundle requires --ledger");
|
|
21444
21744
|
if (!opts.out) throw new ResearchError("research bundle requires --out");
|
|
21445
21745
|
const ledger = resolve19(opts.ledger);
|
|
21446
|
-
const
|
|
21447
|
-
const
|
|
21746
|
+
const { records, entries } = packagedRecords(ledger);
|
|
21747
|
+
const summary = summarizeRecords(records);
|
|
21448
21748
|
if (opts.manifest) {
|
|
21449
|
-
const manifest =
|
|
21749
|
+
const manifest = readFileSync26(resolve19(opts.manifest));
|
|
21450
21750
|
const sha3 = createHash16("sha256").update(manifest).digest("hex");
|
|
21451
21751
|
if (sha3 !== summary.manifest_sha256) throw new ResearchError(`manifest sha256 ${sha3} does not match ledger ${summary.manifest_sha256}`);
|
|
21452
21752
|
entries.push({ name: "manifest.json", bytes: manifest });
|
|
@@ -21494,17 +21794,25 @@ function runResearchBundle(opts) {
|
|
|
21494
21794
|
throw e;
|
|
21495
21795
|
}
|
|
21496
21796
|
}
|
|
21497
|
-
var BLOCK, BUNDLE_VERSION;
|
|
21797
|
+
var BLOCK, BUNDLE_VERSION, TOP_LEVEL_ENTRIES, PAIR_ENTRY_RE, TAR_NAME_MAX, NAME_FIELD, TYPE_OFFSET, PREFIX_OFFSET, PREFIX_FIELD;
|
|
21498
21798
|
var init_bundle = __esm({
|
|
21499
21799
|
"src/research/bundle.ts"() {
|
|
21500
21800
|
"use strict";
|
|
21501
21801
|
init_machine_output();
|
|
21502
21802
|
init_adapter();
|
|
21803
|
+
init_ledger_files();
|
|
21503
21804
|
init_record();
|
|
21504
21805
|
init_report2();
|
|
21505
21806
|
init_summarize();
|
|
21506
21807
|
BLOCK = 512;
|
|
21507
21808
|
BUNDLE_VERSION = 1;
|
|
21809
|
+
TOP_LEVEL_ENTRIES = /* @__PURE__ */ new Set(["summary.json", "report.txt", "provenance.json", "manifest.json"]);
|
|
21810
|
+
PAIR_ENTRY_RE = /^ledger\/pairs\/[^/]+\.json$/;
|
|
21811
|
+
TAR_NAME_MAX = 99;
|
|
21812
|
+
NAME_FIELD = 100;
|
|
21813
|
+
TYPE_OFFSET = 156;
|
|
21814
|
+
PREFIX_OFFSET = 345;
|
|
21815
|
+
PREFIX_FIELD = 155;
|
|
21508
21816
|
}
|
|
21509
21817
|
});
|
|
21510
21818
|
|
|
@@ -22474,7 +22782,7 @@ var init_adapter4 = __esm({
|
|
|
22474
22782
|
|
|
22475
22783
|
// src/adapters/copilot-sdk/changes.ts
|
|
22476
22784
|
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
22477
|
-
import { mkdtempSync as mkdtempSync7, writeFileSync as writeFileSync15, readFileSync as
|
|
22785
|
+
import { mkdtempSync as mkdtempSync7, writeFileSync as writeFileSync15, readFileSync as readFileSync27, rmSync as rmSync13, existsSync as existsSync20, realpathSync as realpathSync12 } from "node:fs";
|
|
22478
22786
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
22479
22787
|
import { basename as basename6, dirname as dirname14, isAbsolute as isAbsolute17, relative as relative12, resolve as resolve22, join as join29 } from "node:path";
|
|
22480
22788
|
function asStr4(v) {
|
|
@@ -22487,13 +22795,13 @@ function relForDisplay5(path, cwd) {
|
|
|
22487
22795
|
function canonicalContainedTarget(abs, root) {
|
|
22488
22796
|
let realRoot;
|
|
22489
22797
|
try {
|
|
22490
|
-
realRoot =
|
|
22798
|
+
realRoot = realpathSync12(root);
|
|
22491
22799
|
} catch (e) {
|
|
22492
22800
|
throw new Error(`trusted repository root ${root} cannot be resolved: ${e instanceof Error ? e.message : String(e)}`);
|
|
22493
22801
|
}
|
|
22494
22802
|
let dir = abs;
|
|
22495
22803
|
const tail = [];
|
|
22496
|
-
while (!
|
|
22804
|
+
while (!existsSync20(dir)) {
|
|
22497
22805
|
const parent = dirname14(dir);
|
|
22498
22806
|
if (parent === dir) break;
|
|
22499
22807
|
tail.unshift(basename6(dir));
|
|
@@ -22501,7 +22809,7 @@ function canonicalContainedTarget(abs, root) {
|
|
|
22501
22809
|
}
|
|
22502
22810
|
let realDir;
|
|
22503
22811
|
try {
|
|
22504
|
-
realDir =
|
|
22812
|
+
realDir = realpathSync12(dir);
|
|
22505
22813
|
} catch (e) {
|
|
22506
22814
|
throw new Error(`write target ${abs} cannot be resolved: ${e instanceof Error ? e.message : String(e)}`);
|
|
22507
22815
|
}
|
|
@@ -22568,7 +22876,7 @@ function reconstructAfterViaGit(rawDiff, display2, before, isCreate) {
|
|
|
22568
22876
|
if (statPath !== TARGET) throw new Error(`write diff reconstructs a different file (${statPath}) than the bound target`);
|
|
22569
22877
|
git8(["--check"]);
|
|
22570
22878
|
git8([]);
|
|
22571
|
-
return op === "delete" ? null :
|
|
22879
|
+
return op === "delete" ? null : readFileSync27(targetPath, "utf8");
|
|
22572
22880
|
} catch (e) {
|
|
22573
22881
|
throw new Error(`could not reconstruct the write to ${display2} from its diff via git apply: ${e instanceof Error ? e.message : String(e)}`);
|
|
22574
22882
|
} finally {
|
|
@@ -23312,7 +23620,7 @@ var init_runtime_qualification = __esm({
|
|
|
23312
23620
|
|
|
23313
23621
|
// src/cli/runtime.ts
|
|
23314
23622
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
23315
|
-
import { existsSync as
|
|
23623
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync13, readFileSync as readFileSync28, writeFileSync as writeFileSync16 } from "node:fs";
|
|
23316
23624
|
import { join as join30, dirname as dirname15 } from "node:path";
|
|
23317
23625
|
function storePath(cwd) {
|
|
23318
23626
|
const dir = gitDir(cwd);
|
|
@@ -23320,9 +23628,9 @@ function storePath(cwd) {
|
|
|
23320
23628
|
}
|
|
23321
23629
|
function readStore(cwd) {
|
|
23322
23630
|
const path = storePath(cwd);
|
|
23323
|
-
if (!path || !
|
|
23631
|
+
if (!path || !existsSync21(path)) return null;
|
|
23324
23632
|
try {
|
|
23325
|
-
const parsed = JSON.parse(
|
|
23633
|
+
const parsed = JSON.parse(readFileSync28(path, "utf8"));
|
|
23326
23634
|
if (parsed && typeof parsed === "object" && parsed.records && typeof parsed.records === "object") return parsed;
|
|
23327
23635
|
} catch {
|
|
23328
23636
|
return null;
|
|
@@ -23482,9 +23790,9 @@ function resolveHookConfigHash(canonicalId, cwd) {
|
|
|
23482
23790
|
"github-copilot-cli": join30(cwd, ".github", "hooks", "tamperward.json")
|
|
23483
23791
|
};
|
|
23484
23792
|
const file = files[canonicalId];
|
|
23485
|
-
if (!file || !
|
|
23793
|
+
if (!file || !existsSync21(file)) return null;
|
|
23486
23794
|
try {
|
|
23487
|
-
const raw =
|
|
23795
|
+
const raw = readFileSync28(file, "utf8");
|
|
23488
23796
|
let hooks = raw;
|
|
23489
23797
|
try {
|
|
23490
23798
|
const parsed = JSON.parse(raw);
|
|
@@ -24490,7 +24798,7 @@ var init_main = __esm({
|
|
|
24490
24798
|
// src/cli/index.ts
|
|
24491
24799
|
init_hook_client();
|
|
24492
24800
|
init_exit();
|
|
24493
|
-
import { readFileSync as
|
|
24801
|
+
import { readFileSync as readFileSync29 } from "node:fs";
|
|
24494
24802
|
function loadMain() {
|
|
24495
24803
|
return Promise.resolve().then(() => (init_main(), main_exports));
|
|
24496
24804
|
}
|
|
@@ -24503,7 +24811,7 @@ async function launch(argv) {
|
|
|
24503
24811
|
if (kind && hookServiceEnabled()) {
|
|
24504
24812
|
let raw = null;
|
|
24505
24813
|
try {
|
|
24506
|
-
raw =
|
|
24814
|
+
raw = readFileSync29(0, "utf8");
|
|
24507
24815
|
} catch {
|
|
24508
24816
|
raw = null;
|
|
24509
24817
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tamperward",
|
|
3
|
-
"version": "2.37.
|
|
3
|
+
"version": "2.37.2",
|
|
4
4
|
"description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "hexrift",
|