tamperward 2.29.13 → 2.29.14
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 +66 -16
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17883,6 +17883,22 @@ function parseStraceFileAccess(raw) {
|
|
|
17883
17883
|
}
|
|
17884
17884
|
return out3;
|
|
17885
17885
|
}
|
|
17886
|
+
function tracerDenialDiagnostic(stderr) {
|
|
17887
|
+
for (const raw of stderr.split("\n")) {
|
|
17888
|
+
const line = raw.trim();
|
|
17889
|
+
if (line.startsWith("strace:") && TRACER_DENIAL_RE.test(line)) return line;
|
|
17890
|
+
}
|
|
17891
|
+
return null;
|
|
17892
|
+
}
|
|
17893
|
+
function classifyTraceRun(r) {
|
|
17894
|
+
if (r.spawnError) return { kind: "tracer-error", diagnostic: `strace could not be launched: ${r.spawnError}` };
|
|
17895
|
+
const denial = tracerDenialDiagnostic(r.stderr);
|
|
17896
|
+
if (denial) return { kind: "tracer-error", diagnostic: denial };
|
|
17897
|
+
if (r.signal) return { kind: "tracer-error", diagnostic: `strace was killed by ${r.signal} before the trace completed` };
|
|
17898
|
+
if (r.traceFileCount === 0) return { kind: "tracer-error", diagnostic: "strace produced no trace files; the tracer did not attach (ptrace may be denied)" };
|
|
17899
|
+
if (r.accessCount === 0) return { kind: "tracer-error", diagnostic: "strace recorded no file syscalls; the tracer attached to nothing (ptrace may be denied)" };
|
|
17900
|
+
return { kind: "ok", exit: r.status ?? 1 };
|
|
17901
|
+
}
|
|
17886
17902
|
function inside3(root, path) {
|
|
17887
17903
|
const rel = relative6(resolve11(root), resolve11(path));
|
|
17888
17904
|
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep4}`) && !isAbsolute12(rel);
|
|
@@ -18009,11 +18025,9 @@ function rewriteTraceRoot(accesses, actualRoot) {
|
|
|
18009
18025
|
return { ...item, path: join22(TRACE_ROOT, ...rel.split("/")) };
|
|
18010
18026
|
});
|
|
18011
18027
|
}
|
|
18012
|
-
function
|
|
18013
|
-
const root = mkdtempSync5(join22(tmpdir6(), "tw-trace-base-"));
|
|
18028
|
+
function straceOnce(root, command, budget) {
|
|
18014
18029
|
const traceDir = mkdtempSync5(join22(tmpdir6(), "tw-trace-log-"));
|
|
18015
18030
|
try {
|
|
18016
|
-
materializeBase(base, cwd, root);
|
|
18017
18031
|
const prefix = join22(traceDir, "trace");
|
|
18018
18032
|
const traced = spawnSync5(
|
|
18019
18033
|
"strace",
|
|
@@ -18042,21 +18056,40 @@ function runOneTrace(cwd, base, command, budget) {
|
|
|
18042
18056
|
maxBuffer: 64 * 1024 * 1024
|
|
18043
18057
|
}
|
|
18044
18058
|
);
|
|
18045
|
-
|
|
18046
|
-
const logs = traceFiles(prefix);
|
|
18047
|
-
if (logs.length === 0) {
|
|
18048
|
-
throw new Error("strace produced no trace files");
|
|
18049
|
-
}
|
|
18059
|
+
const logs = traced.error ? [] : traceFiles(prefix);
|
|
18050
18060
|
const accesses = logs.flatMap((path) => parseStraceFileAccess(readFileSync20(path, "utf8")));
|
|
18051
|
-
|
|
18052
|
-
|
|
18053
|
-
|
|
18061
|
+
const raw = {
|
|
18062
|
+
spawnError: traced.error ? traced.error.message : null,
|
|
18063
|
+
status: traced.status,
|
|
18064
|
+
signal: traced.signal ?? null,
|
|
18065
|
+
stderr: String(traced.stderr ?? "").slice(0, 8192),
|
|
18066
|
+
traceFileCount: logs.length,
|
|
18067
|
+
accessCount: accesses.length
|
|
18054
18068
|
};
|
|
18069
|
+
return { raw, accesses };
|
|
18055
18070
|
} finally {
|
|
18056
|
-
rmSync9(root, { recursive: true, force: true });
|
|
18057
18071
|
rmSync9(traceDir, { recursive: true, force: true });
|
|
18058
18072
|
}
|
|
18059
18073
|
}
|
|
18074
|
+
function runOneTrace(cwd, base, command, budget) {
|
|
18075
|
+
const root = mkdtempSync5(join22(tmpdir6(), "tw-trace-base-"));
|
|
18076
|
+
try {
|
|
18077
|
+
materializeBase(base, cwd, root);
|
|
18078
|
+
const { raw, accesses } = straceOnce(root, command, budget);
|
|
18079
|
+
return { outcome: classifyTraceRun(raw), accesses: rewriteTraceRoot(accesses, root) };
|
|
18080
|
+
} finally {
|
|
18081
|
+
rmSync9(root, { recursive: true, force: true });
|
|
18082
|
+
}
|
|
18083
|
+
}
|
|
18084
|
+
function tracerPreflight(budget = 10) {
|
|
18085
|
+
const probeRoot = mkdtempSync5(join22(tmpdir6(), "tw-trace-probe-"));
|
|
18086
|
+
try {
|
|
18087
|
+
const { raw } = straceOnce(probeRoot, "true", Math.max(1, Math.min(budget, 10)));
|
|
18088
|
+
return classifyTraceRun(raw);
|
|
18089
|
+
} finally {
|
|
18090
|
+
rmSync9(probeRoot, { recursive: true, force: true });
|
|
18091
|
+
}
|
|
18092
|
+
}
|
|
18060
18093
|
function renderText2(report2) {
|
|
18061
18094
|
const out3 = (s = "") => void process.stdout.write(s + "\n");
|
|
18062
18095
|
out3("tamperward trace-verify \u2014 ADVISORY observation, not an enforcement verdict");
|
|
@@ -18143,6 +18176,15 @@ function runTraceVerify(opts = {}) {
|
|
|
18143
18176
|
`);
|
|
18144
18177
|
return 2;
|
|
18145
18178
|
}
|
|
18179
|
+
const preflight = tracerPreflight(budget);
|
|
18180
|
+
if (preflight.kind === "tracer-error") {
|
|
18181
|
+
process.stderr.write(
|
|
18182
|
+
`tamperward trace-verify: the tracer cannot observe in this environment, so no verifier input can be discovered \u2014 ${preflight.diagnostic}.
|
|
18183
|
+
This is a tracing/tooling limitation (e.g. ptrace denied by seccomp or Yama), not a verifier result.
|
|
18184
|
+
`
|
|
18185
|
+
);
|
|
18186
|
+
return 2;
|
|
18187
|
+
}
|
|
18146
18188
|
const atBase = trackedAt(base, cwd);
|
|
18147
18189
|
const tracked = new Set(atBase);
|
|
18148
18190
|
const covered = verifierCoveredInputs(command, atBase, policy);
|
|
@@ -18150,9 +18192,16 @@ function runTraceVerify(opts = {}) {
|
|
|
18150
18192
|
const runExits = [];
|
|
18151
18193
|
try {
|
|
18152
18194
|
for (let i = 0; i < runs; i++) {
|
|
18153
|
-
const
|
|
18154
|
-
|
|
18155
|
-
|
|
18195
|
+
const { outcome, accesses } = runOneTrace(cwd, base, command, budget);
|
|
18196
|
+
if (outcome.kind === "tracer-error") {
|
|
18197
|
+
process.stderr.write(
|
|
18198
|
+
`tamperward trace-verify: tracing failed during run ${i + 1}/${runs} \u2014 ${outcome.diagnostic}. No advisory report emitted.
|
|
18199
|
+
`
|
|
18200
|
+
);
|
|
18201
|
+
return 2;
|
|
18202
|
+
}
|
|
18203
|
+
observed.push(accesses);
|
|
18204
|
+
runExits.push(outcome.exit);
|
|
18156
18205
|
}
|
|
18157
18206
|
} catch (e) {
|
|
18158
18207
|
process.stderr.write(`tamperward trace-verify: tracing failed (${e instanceof Error ? e.message : String(e)}).
|
|
@@ -18199,7 +18248,7 @@ function parseTraceVerify(args) {
|
|
|
18199
18248
|
}
|
|
18200
18249
|
return out3;
|
|
18201
18250
|
}
|
|
18202
|
-
var TRACE_ROOT, CONFIG_BASENAMES;
|
|
18251
|
+
var TRACE_ROOT, TRACER_DENIAL_RE, CONFIG_BASENAMES;
|
|
18203
18252
|
var init_trace_verify = __esm({
|
|
18204
18253
|
"src/cli/trace-verify.ts"() {
|
|
18205
18254
|
"use strict";
|
|
@@ -18209,6 +18258,7 @@ var init_trace_verify = __esm({
|
|
|
18209
18258
|
init_policy_load();
|
|
18210
18259
|
init_verify();
|
|
18211
18260
|
TRACE_ROOT = "/__tamperward_trusted_base__";
|
|
18261
|
+
TRACER_DENIAL_RE = /\b(?:PTRACE_[A-Z_]+|ptrace|seccomp|no[-_ ]new[-_ ]privs|yama)\b|Operation not permitted|Permission denied/i;
|
|
18212
18262
|
CONFIG_BASENAMES = /* @__PURE__ */ new Set([
|
|
18213
18263
|
"package.json",
|
|
18214
18264
|
"pytest.ini",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tamperward",
|
|
3
|
-
"version": "2.29.
|
|
3
|
+
"version": "2.29.14",
|
|
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",
|