Merge commit '316d8b7480c2e10925ebdff8310fe26ef5a881a8' into torch/session-manager

This commit is contained in:
thePR0M3TH3AN committed 2026-10-01 10:51:39 -04:00
commit f608e50556
5 files changed
+901

No files matched your search

+80
View File
@@ -2635,3 +2635,83 @@ test_integrity_note:
did_relax_any_assertion: false
if_true_explain_spec_basis: ""
```
# Candidate engine and artifact sealing Phase A (2026-10-01)
```yaml
test_integrity_note:
change_type: new_tests
scenarios:
- id: SCN-candidate-engine-pure-fixture
given: Hermetic frozen fixture descriptors, bounded command data, explicit environment and a fixture artifact directory
when: The isolated Phase-A engine observes a child execution
then: Only bounded parent-observed terminal data and a nonpromotable SourceAttestation are returned
- id: SCN-artifact-manifest-seal
given: Fixed regular artifact bytes in a hermetic temporary directory
when: The parent seals the directory
then: Canonical sorted paths, byte lengths and SHA-256 values are write-once and verify unchanged
observable_outcomes:
- Raw exit, error, signal, timeout and overflow observations; zero exit plus overflow is not success
- No receipt identity, writer capability, registered-state handle or consumer eligibility
- Capped canonical manifests and refusals for links, traversal, special files, overwrite and post-seal drift
determinism_controls:
- Fixed bytes, injected executors and isolated temporary directories
- No retries, sleeps, network, registered control-plane state or provider process
anti_cheat_rationale:
prevents:
- Hard-coded PASS or fixture receipt promotion
- Ambient environment or registered-state capability leakage
- Snapshot/golden rubber-stamping and unbounded output masking
- Link, traversal, overwrite or post-seal artifact substitution
relaxation:
did_relax_any_assertion: false
if_true_explain_spec_basis: ""
```
# Candidate engine and artifact sealing Phase A corrective boundaries (2026-10-01)
```yaml
test_integrity_note:
change_type: new_tests
scenarios:
- id: SCN-candidate-engine-boundary-limits
given: "A fixture run with candidate execution limits"
when: "A required bound is missing, zero, oversized, or supplied to the injected native-shaped executor"
then: "Execution refuses before start or receives finite timeout and output caps without ambient defaults"
- id: SCN-candidate-engine-utf8-overflow
given: "A parent observes multi-byte UTF-8 output larger than its byte cap"
when: "The child exits zero with output overflow"
then: "Only a valid byte-bounded prefix is retained and the terminal observation is non-success"
- id: SCN-candidate-engine-immutable-input-and-environment
given: "A candidate input byte snapshot and explicit deterministic environment"
when: "The digest or byte length is absent or mismatched, an unallowlisted environment is requested, or caller data mutates"
then: "The run refuses before execution or preserves its independent immutable snapshot"
- id: SCN-artifact-manifest-bytewise-and-caps
given: "Fixed artifacts with bytewise-distinct UTF-8 paths and configured evidence limits"
when: "The parent seals files at or beyond count, file, aggregate, path, and manifest limits"
then: "Canonical UTF-8 byte ordering is stable and every over-limit artifact refuses"
- id: SCN-artifact-manifest-root-and-manifest-boundaries
given: "A candidate artifact root or final manifest path"
when: "A link, directory/special node, pre-existing seal, or oversized manifest is encountered"
then: "No path is followed or parsed as evidence and write-once sealing refuses"
- id: SCN-artifact-manifest-complete-drift
given: "A previously sealed manifest"
when: "An artifact is added or the manifest is replaced"
then: "Verification rejects drift or an invalid seal"
observable_outcomes:
- "UTF-8 retained bytes never exceed maxOutputBytes; overflow including exit zero is non-success"
- "Missing or mismatched input byte snapshots, unallowlisted environment names, and invalid bounds do not invoke the executor"
- "Manifest lstat/open/read caps, path ordering, link/special refusal, and seal-drift errors are externally observable"
determinism_controls:
- "Injected fixture executor, fixed byte buffers, temporary directories, and no ambient environment inheritance"
- "No network, registered control-plane state, provider process, or live receipt adapter"
anti_cheat_rationale:
prevents:
- "Character-count truncation retaining more bytes than the declared cap"
- "Ambient or syntactically valid but unapproved environment capability leakage"
- "Caller-supplied digest labels impersonating verified immutable input"
- "Link-following, unbounded manifest parsing, locale-dependent ordering, or post-seal substitution"
relaxation:
did_relax_any_assertion: false
if_true_explain_spec_basis: ""
```
+218
View File
@@ -0,0 +1,218 @@
import { createHash } from 'node:crypto';
import {
closeSync, constants, fstatSync, lstatSync, openSync, readdirSync, readSync, realpathSync, writeFileSync,
} from 'node:fs';
import { isAbsolute, join, relative, resolve } from 'node:path';
import { TorchError } from '../kernel/errors.mjs';
export const ARTIFACT_MANIFEST_FILE = '.torch-artifact-manifest-v1.json';
export const ARTIFACT_MANIFEST_LIMITS = Object.freeze({
maxFiles: 64,
maxFileBytes: 1_048_576,
maxTotalBytes: 8_388_608,
maxPathBytes: 4_096,
maxManifestBytes: 65_536,
});
function fail(message, code, details) {
throw new TorchError(message, { code, details });
}
function canonicalJson(value) {
return JSON.stringify(value);
}
function digest(value) {
return createHash('sha256').update(canonicalJson(value)).digest('hex');
}
function limits(input = {}) {
if (!input || typeof input !== 'object' || Array.isArray(input)) fail('Artifact limits must be an object', 'CANDIDATE_ARTIFACT_LIMIT_INVALID');
const allowed = new Set(Object.keys(ARTIFACT_MANIFEST_LIMITS));
for (const key of Object.keys(input)) {
if (!allowed.has(key)) fail('Artifact limit is unsupported', 'CANDIDATE_ARTIFACT_LIMIT_INVALID', { key });
}
const result = {};
for (const [key, maximum] of Object.entries(ARTIFACT_MANIFEST_LIMITS)) {
const value = input[key] ?? maximum;
if (!Number.isInteger(value) || value < 1 || value > maximum) fail('Artifact limit is invalid', 'CANDIDATE_ARTIFACT_LIMIT_INVALID', { key, value, maximum });
result[key] = value;
}
return result;
}
function bytewisePathOrder(left, right) {
return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
}
export function assertArtifactRelativePath(path, maxPathBytes = ARTIFACT_MANIFEST_LIMITS.maxPathBytes) {
if (typeof path !== 'string' || !path || isAbsolute(path) || path.includes('\\')
|| path.includes('\0') || Buffer.byteLength(path, 'utf8') > maxPathBytes
|| path.split('/').some((part) => !part || part === '.' || part === '..')) {
fail('Artifact path is not canonical relative evidence', 'CANDIDATE_ARTIFACT_PATH_INVALID', { path });
}
return path;
}
export function artifactNodeKind(stat, path) {
if (stat.isSymbolicLink()) fail('Artifact cannot be a link', 'CANDIDATE_ARTIFACT_LINK_REJECTED', { path });
if (stat.isDirectory()) return 'directory';
if (stat.isFile()) return 'file';
fail('Artifact must be a regular file or directory', 'CANDIDATE_ARTIFACT_SPECIAL_REJECTED', { path });
}
function lstatOrNull(path) {
try { return lstatSync(path); }
catch (error) {
if (error?.code === 'ENOENT') return null;
throw error;
}
}
function readRegularFileCapped(path, limit, label) {
const beforePath = lstatOrNull(path);
if (!beforePath) fail(`${label} is missing`, 'CANDIDATE_ARTIFACT_MANIFEST_MISSING', { path });
artifactNodeKind(beforePath, path);
if (!beforePath.isFile()) fail(`${label} must be a regular file`, 'CANDIDATE_ARTIFACT_SPECIAL_REJECTED', { path });
if (beforePath.size > limit) fail(`${label} exceeds its byte limit`, 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED', { path, limit });
let descriptor;
try {
descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
const before = fstatSync(descriptor);
if (!before.isFile()) fail(`${label} must be a regular file`, 'CANDIDATE_ARTIFACT_SPECIAL_REJECTED', { path });
if (before.size > limit) fail(`${label} exceeds its byte limit`, 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED', { path, limit });
const buffer = Buffer.alloc(before.size + 1);
let offset = 0;
while (offset < buffer.length) {
const read = readSync(descriptor, buffer, offset, buffer.length - offset, offset);
if (read === 0) break;
offset += read;
}
const after = fstatSync(descriptor);
if (after.size !== before.size || offset !== before.size) {
fail(`${label} changed while being read`, 'CANDIDATE_ARTIFACT_SEAL_DRIFT', { path });
}
return buffer.subarray(0, before.size);
} catch (error) {
if (error instanceof TorchError) throw error;
fail(`${label} cannot be read as a regular file`, 'CANDIDATE_ARTIFACT_LINK_REJECTED', { path });
} finally {
if (descriptor !== undefined) closeSync(descriptor);
}
}
function inventory(root, configuredLimits) {
const files = [];
let totalBytes = 0;
function visit(current, parent = '') {
for (const name of readdirSync(current).sort(bytewisePathOrder)) {
if (!parent && name === ARTIFACT_MANIFEST_FILE) continue;
const path = parent ? `${parent}/${name}` : name;
assertArtifactRelativePath(path, configuredLimits.maxPathBytes);
const absolute = join(current, name);
const stat = lstatSync(absolute);
const kind = artifactNodeKind(stat, path);
if (kind === 'directory') visit(absolute, path);
else {
if (stat.size > configuredLimits.maxFileBytes) fail('Artifact exceeds per-file limit', 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED', { path });
totalBytes += stat.size;
if (files.length + 1 > configuredLimits.maxFiles || totalBytes > configuredLimits.maxTotalBytes) {
fail('Artifact manifest exceeds aggregate limits', 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED');
}
const bytes = readRegularFileCapped(absolute, configuredLimits.maxFileBytes, 'Artifact');
if (bytes.length !== stat.size || bytes.length > configuredLimits.maxFileBytes) {
fail('Artifact changed while being read', 'CANDIDATE_ARTIFACT_SEAL_DRIFT', { path });
}
files.push({ path, bytes: bytes.length, sha256: createHash('sha256').update(bytes).digest('hex') });
}
}
}
visit(root);
return { files: files.sort((left, right) => bytewisePathOrder(left.path, right.path)), totalBytes };
}
function validateManifest(manifest, configuredLimits) {
if (!manifest || manifest.schema !== 'torch.dev/artifact-manifest/v1alpha1' || !Array.isArray(manifest.files)
|| !Number.isInteger(manifest.totalBytes) || typeof manifest.sealDigest !== 'string') {
fail('Artifact manifest is malformed', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID');
}
const manifestKeys = new Set(['schema', 'files', 'totalBytes', 'sealDigest']);
for (const key of Object.keys(manifest)) {
if (!manifestKeys.has(key)) fail('Artifact manifest contains unsupported fields', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID', { key });
}
const seen = new Set();
let totalBytes = 0;
for (const record of manifest.files) {
if (!record || typeof record !== 'object' || Array.isArray(record)
|| Object.keys(record).some((key) => !new Set(['path', 'bytes', 'sha256']).has(key))) {
fail('Artifact manifest record is invalid', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID');
}
assertArtifactRelativePath(record.path, configuredLimits.maxPathBytes);
if (seen.has(record.path)) fail('Artifact manifest contains duplicate paths', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID');
seen.add(record.path);
if (!Number.isInteger(record.bytes) || record.bytes < 0 || record.bytes > configuredLimits.maxFileBytes
|| !/^[0-9a-f]{64}$/.test(record.sha256 ?? '')) fail('Artifact manifest record is invalid', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID');
totalBytes += record.bytes;
}
if (manifest.files.length > configuredLimits.maxFiles || totalBytes !== manifest.totalBytes || totalBytes > configuredLimits.maxTotalBytes) {
fail('Artifact manifest limits do not match', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID');
}
const sorted = [...manifest.files].sort((left, right) => bytewisePathOrder(left.path, right.path));
if (canonicalJson(sorted) !== canonicalJson(manifest.files)) fail('Artifact manifest paths are not canonical', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID');
const payload = { schema: manifest.schema, files: manifest.files, totalBytes: manifest.totalBytes };
if (digest(payload) !== manifest.sealDigest) fail('Artifact manifest seal digest is invalid', 'CANDIDATE_ARTIFACT_SEAL_INVALID');
return payload;
}
function ownedRoot(directory) {
if (typeof directory !== 'string' || !directory) fail('Artifact directory is missing', 'CANDIDATE_ARTIFACT_DIRECTORY_INVALID');
const initial = lstatOrNull(directory);
if (!initial) fail('Artifact directory is missing', 'CANDIDATE_ARTIFACT_DIRECTORY_INVALID');
if (initial.isSymbolicLink()) fail('Artifact directory cannot be a link', 'CANDIDATE_ARTIFACT_LINK_REJECTED', { directory });
if (!initial.isDirectory()) fail('Artifact directory is not a directory', 'CANDIDATE_ARTIFACT_DIRECTORY_INVALID');
const root = realpathSync(directory);
if (!lstatSync(root).isDirectory()) fail('Artifact directory is not a directory', 'CANDIDATE_ARTIFACT_DIRECTORY_INVALID');
return root;
}
export function sealArtifactManifest({ directory, limits: requestedLimits } = {}) {
const root = ownedRoot(directory);
const configuredLimits = limits(requestedLimits);
const path = join(root, ARTIFACT_MANIFEST_FILE);
if (lstatOrNull(path)) fail('Artifact manifest is already sealed', 'CANDIDATE_ARTIFACT_ALREADY_SEALED');
const records = inventory(root, configuredLimits);
const payload = { schema: 'torch.dev/artifact-manifest/v1alpha1', ...records };
const manifest = { ...payload, sealDigest: digest(payload) };
const serialized = Buffer.from(`${canonicalJson(manifest)}\n`, 'utf8');
if (serialized.length > configuredLimits.maxManifestBytes) {
fail('Artifact manifest exceeds its byte limit', 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED');
}
writeFileSync(path, serialized, { mode: 0o600, flag: 'wx' });
return Object.freeze({ ...manifest, manifestPath: path });
}
export function verifyArtifactManifest({ directory, limits: requestedLimits } = {}) {
const root = ownedRoot(directory);
const configuredLimits = limits(requestedLimits);
const path = join(root, ARTIFACT_MANIFEST_FILE);
const manifestStat = lstatOrNull(path);
if (!manifestStat) fail('Artifact manifest is missing', 'CANDIDATE_ARTIFACT_MANIFEST_MISSING');
artifactNodeKind(manifestStat, ARTIFACT_MANIFEST_FILE);
if (!manifestStat.isFile()) fail('Artifact manifest must be a regular file', 'CANDIDATE_ARTIFACT_SPECIAL_REJECTED');
if (manifestStat.size > configuredLimits.maxManifestBytes) {
fail('Artifact manifest exceeds its byte limit', 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED');
}
let manifest;
try { manifest = JSON.parse(readRegularFileCapped(path, configuredLimits.maxManifestBytes, 'Artifact manifest').toString('utf8')); }
catch { fail('Artifact manifest cannot be parsed', 'CANDIDATE_ARTIFACT_MANIFEST_INVALID'); }
const payload = validateManifest(manifest, configuredLimits);
const current = inventory(root, configuredLimits);
if (canonicalJson(current) !== canonicalJson({ files: payload.files, totalBytes: payload.totalBytes })) {
fail('Artifact contents changed after sealing', 'CANDIDATE_ARTIFACT_SEAL_DRIFT');
}
if (resolve(path) !== join(root, ARTIFACT_MANIFEST_FILE) || relative(root, path).startsWith('..')) {
fail('Artifact manifest escaped its directory', 'CANDIDATE_ARTIFACT_DIRECTORY_INVALID');
}
return Object.freeze({ ...manifest, manifestPath: path, verified: true });
}
+224
View File
@@ -0,0 +1,224 @@
import { spawnSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { TorchError } from '../kernel/errors.mjs';
export const CANDIDATE_ENGINE_LIMITS = Object.freeze({
maxInputBytes: 1_048_576,
maxDefinitionBytes: 65_536,
maxArgs: 64,
maxArgBytes: 4_096,
maxEnvironmentEntries: 3,
maxEnvironmentNameBytes: 128,
maxEnvironmentValueBytes: 4_096,
maxOutputBytes: 65_536,
maxArtifactPathBytes: 4_096,
timeoutMs: 60_000,
});
/** Deterministic variables only; no ambient process environment is inherited. */
export const CANDIDATE_ENGINE_ENV_ALLOWLIST = Object.freeze({
LANG: Object.freeze(['C.UTF-8']),
LC_ALL: Object.freeze(['C']),
TZ: Object.freeze(['UTC']),
});
function fail(message, code, details) {
throw new TorchError(message, { code, details });
}
function byteLength(value) {
return Buffer.byteLength(value, 'utf8');
}
function sha256(bytes) {
return createHash('sha256').update(bytes).digest('hex');
}
function exactObject(value, allowed, label) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
fail(`${label} must be an object`, 'CANDIDATE_ENGINE_INPUT_INVALID');
}
for (const key of Object.keys(value)) {
if (!allowed.has(key)) fail(`${label} contains unsupported capability data`, 'CANDIDATE_ENGINE_CAPABILITY_FORBIDDEN', { key });
}
}
function boundedString(value, limit, label) {
if (typeof value !== 'string' || !value || byteLength(value) > limit) {
fail(`${label} is missing or exceeds its limit`, 'CANDIDATE_ENGINE_INPUT_INVALID', { label, limit });
}
return value;
}
function normalizeLimits(limits) {
exactObject(limits, new Set(Object.keys(CANDIDATE_ENGINE_LIMITS)), 'limits');
const normalized = {};
for (const [key, maximum] of Object.entries(CANDIDATE_ENGINE_LIMITS)) {
if (!Object.hasOwn(limits, key)) {
fail(`Candidate-engine limit is required: ${key}`, 'CANDIDATE_ENGINE_LIMIT_INVALID', { key });
}
const value = limits[key];
if (!Number.isInteger(value) || value < 1 || value > maximum) {
fail(`Invalid candidate-engine limit: ${key}`, 'CANDIDATE_ENGINE_LIMIT_INVALID', { key, value, maximum });
}
normalized[key] = value;
}
return Object.freeze(normalized);
}
function freeze(value) {
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
for (const item of Object.values(value)) freeze(item);
Object.freeze(value);
}
return value;
}
function normalizeInput(input, limits) {
exactObject(input, new Set(['directory', 'digest', 'bytes', 'byteLength']), 'input');
const directory = boundedString(input.directory, limits.maxArtifactPathBytes, 'input.directory');
if (!Buffer.isBuffer(input.bytes) && !(input.bytes instanceof Uint8Array)) {
fail('Candidate input bytes are required', 'CANDIDATE_ENGINE_INPUT_INVALID');
}
const snapshot = Buffer.from(input.bytes);
if (snapshot.length > limits.maxInputBytes || !Number.isInteger(input.byteLength)
|| input.byteLength !== snapshot.length || typeof input.digest !== 'string'
|| !/^[0-9a-f]{64}$/i.test(input.digest)) {
fail('Candidate input must be a bounded immutable byte snapshot', 'CANDIDATE_ENGINE_INPUT_INVALID');
}
const observedDigest = sha256(snapshot);
if (input.digest.toLowerCase() !== observedDigest) {
fail('Candidate input digest does not bind the supplied bytes', 'CANDIDATE_ENGINE_INPUT_INVALID');
}
return freeze({ directory, digest: observedDigest, bytes: snapshot.length });
}
function normalizeDefinition(definition, limits) {
exactObject(definition, new Set(['command', 'args']), 'definition');
const command = boundedString(definition.command, limits.maxArgBytes, 'definition.command');
const args = normalizeArgs(definition.args ?? [], limits, 'definition.args');
const normalized = { command, args };
if (byteLength(JSON.stringify(normalized)) > limits.maxDefinitionBytes) {
fail('Candidate definition exceeds its limit', 'CANDIDATE_ENGINE_INPUT_INVALID');
}
return freeze(normalized);
}
function normalizeArgs(args, limits, label = 'args') {
if (!Array.isArray(args) || args.length > limits.maxArgs) {
fail(`${label} exceeds its count limit`, 'CANDIDATE_ENGINE_INPUT_INVALID');
}
return freeze(args.map((value) => boundedString(value, limits.maxArgBytes, label)));
}
function normalizeEnvironment(environment, limits) {
exactObject(environment, new Set(Object.keys(environment ?? {})), 'environment');
const entries = Object.entries(environment);
if (entries.length > limits.maxEnvironmentEntries) {
fail('Candidate environment exceeds its entry limit', 'CANDIDATE_ENGINE_INPUT_INVALID');
}
const normalized = {};
for (const [key, value] of entries.sort(([left], [right]) => Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8')))) {
const allowedValues = CANDIDATE_ENGINE_ENV_ALLOWLIST[key];
if (!allowedValues || byteLength(key) > limits.maxEnvironmentNameBytes) {
fail('Candidate environment name is not allowlisted', 'CANDIDATE_ENGINE_ENVIRONMENT_INVALID', { key });
}
const bounded = boundedString(value, limits.maxEnvironmentValueBytes, `environment.${key}`);
if (!allowedValues.includes(bounded)) {
fail('Candidate environment value is not allowlisted', 'CANDIDATE_ENGINE_ENVIRONMENT_INVALID', { key });
}
normalized[key] = bounded;
}
return freeze(normalized);
}
function safeUtf8Prefix(bytes, limit) {
const capped = Math.min(bytes.length, limit);
const decoder = new TextDecoder('utf-8', { fatal: true });
for (let length = capped; length >= 0; length -= 1) {
try {
const text = decoder.decode(bytes.subarray(0, length));
if (byteLength(text) <= limit) return text;
} catch {
// Shorten until the prefix ends on a complete UTF-8 sequence.
}
}
return '';
}
function boundedOutput(value, limit) {
const output = Buffer.isBuffer(value) ? Buffer.from(value)
: Buffer.from(typeof value === 'string' ? value : value?.toString?.() ?? '', 'utf8');
const bytes = output.length;
const text = safeUtf8Prefix(output, bytes > limit ? limit : bytes);
return freeze({ bytes, retainedBytes: byteLength(text), text, overflow: bytes > limit });
}
function normalizeError(error) {
if (!error) return null;
return freeze({
code: typeof error.code === 'string' ? boundedOutput(error.code, 128).text : null,
message: boundedOutput(typeof error.message === 'string' ? error.message : String(error), 512).text,
});
}
function terminalObservation(result, limits) {
const stdout = boundedOutput(result?.stdout, limits.maxOutputBytes);
const stderr = boundedOutput(result?.stderr, limits.maxOutputBytes);
const error = normalizeError(result?.error);
const signal = typeof result?.signal === 'string' ? result.signal : null;
const exitStatus = Number.isInteger(result?.status) ? result.status : null;
const timedOut = result?.timedOut === true || error?.code === 'ETIMEDOUT';
const overflow = stdout.overflow || stderr.overflow || error?.code === 'ENOBUFS';
let kind = 'exit';
if (timedOut) kind = 'timeout';
else if (overflow) kind = 'overflow';
else if (signal) kind = 'signal';
else if (error) kind = 'error';
return freeze({
kind, exitStatus, signal, error, timedOut, overflow,
success: kind === 'exit' && exitStatus === 0,
stdout, stderr,
});
}
/**
* Fixture-only Phase-A seam. It deliberately exposes a raw parent observation,
* never a receipt, capability, ControlPlane handle, or registered project root.
*/
export function runCandidateEngine(options = {}) {
exactObject(options, new Set(['input', 'definition', 'args', 'environment', 'artifactDirectory', 'executor', 'limits']), 'candidate engine options');
const limits = normalizeLimits(options.limits);
const input = normalizeInput(options.input, limits);
const definition = normalizeDefinition(options.definition, limits);
const args = normalizeArgs(options.args ?? [], limits);
const environment = normalizeEnvironment(options.environment ?? {}, limits);
const artifactDirectory = boundedString(options.artifactDirectory, limits.maxArtifactPathBytes, 'artifactDirectory');
const executor = options.executor ?? ((command, commandArgs, execution) => spawnSync(command, commandArgs, execution));
if (typeof executor !== 'function') fail('Candidate executor must be a function', 'CANDIDATE_ENGINE_INPUT_INVALID');
const commandArgs = freeze([...definition.args, ...args]);
const execution = freeze({
cwd: input.directory,
env: environment,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
maxBuffer: limits.maxOutputBytes,
timeout: limits.timeoutMs,
killSignal: 'SIGTERM',
artifactDirectory,
});
let raw;
try {
raw = executor(definition.command, commandArgs, execution);
} catch (error) {
raw = { status: null, error };
}
const terminal = terminalObservation(raw, limits);
return freeze({
schema: 'torch.dev/candidate-engine-observation/v1alpha1',
fixtureOnly: true,
sourceAttestation: freeze({ schema: 'torch.dev/source-attestation/v1alpha1', promotion: 'nonpromotable' }),
input, definition, args, environment, artifactDirectory, limits, terminal,
});
}
@@ -0,0 +1,193 @@
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import {
mkdirSync, mkdtempSync, readFileSync, renameSync, symlinkSync, unlinkSync, writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import {
ARTIFACT_MANIFEST_FILE, artifactNodeKind, assertArtifactRelativePath, sealArtifactManifest, verifyArtifactManifest,
} from '../../src/checks/artifact-manifest.mjs';
function fixture() {
return mkdtempSync(join(tmpdir(), 'torch-candidate-manifest-'));
}
function write(root, path, content) {
const target = join(root, path);
mkdirSync(join(target, '..'), { recursive: true });
writeFileSync(target, content);
}
function manifestBytesFor(path, content) {
const bytes = Buffer.from(content, 'utf8');
const payload = {
schema: 'torch.dev/artifact-manifest/v1alpha1',
files: [{ path, bytes: bytes.length, sha256: createHash('sha256').update(bytes).digest('hex') }],
totalBytes: bytes.length,
};
const manifest = {
...payload,
sealDigest: createHash('sha256').update(JSON.stringify(payload)).digest('hex'),
};
return Buffer.byteLength(`${JSON.stringify(manifest)}\n`, 'utf8');
}
test('SCN-artifact-manifest-seal: regular fixture artifacts seal as canonical sorted hash and length evidence regardless of creation order', () => {
const first = fixture();
const second = fixture();
write(first, 'z/proof.txt', 'zeta');
write(first, 'a/proof.txt', 'alpha');
write(second, 'a/proof.txt', 'alpha');
write(second, 'z/proof.txt', 'zeta');
const left = sealArtifactManifest({ directory: first });
const right = sealArtifactManifest({ directory: second });
assert.deepEqual(left.files, right.files);
assert.equal(left.sealDigest, right.sealDigest);
assert.deepEqual(left.files.map((file) => file.path), ['a/proof.txt', 'z/proof.txt']);
assert.equal(left.totalBytes, 9);
assert.equal(verifyArtifactManifest({ directory: first }).verified, true);
assert.throws(() => sealArtifactManifest({ directory: first }), { code: 'CANDIDATE_ARTIFACT_ALREADY_SEALED' });
});
test('SCN-artifact-manifest-negative-paths: traversal, links, special files, caps and duplicate manifest paths refuse', () => {
assert.throws(() => assertArtifactRelativePath('../escape'), { code: 'CANDIDATE_ARTIFACT_PATH_INVALID' });
assert.throws(() => assertArtifactRelativePath('/absolute'), { code: 'CANDIDATE_ARTIFACT_PATH_INVALID' });
const linked = fixture();
write(linked, 'safe.txt', 'safe');
symlinkSync('safe.txt', join(linked, 'linked.txt'));
assert.throws(() => sealArtifactManifest({ directory: linked }), { code: 'CANDIDATE_ARTIFACT_LINK_REJECTED' });
const limited = fixture();
write(limited, 'large.txt', '12345');
assert.throws(() => sealArtifactManifest({ directory: limited, limits: { maxFileBytes: 4 } }),
{ code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
assert.throws(() => artifactNodeKind({
isSymbolicLink: () => false, isDirectory: () => false, isFile: () => false,
}, 'pipe'), { code: 'CANDIDATE_ARTIFACT_SPECIAL_REJECTED' });
const malformed = fixture();
write(malformed, 'proof.txt', 'proof');
sealArtifactManifest({ directory: malformed });
const path = join(malformed, ARTIFACT_MANIFEST_FILE);
const record = JSON.parse(readFileSync(path, 'utf8'));
record.files.push({ ...record.files[0] });
writeFileSync(path, JSON.stringify(record));
assert.throws(() => verifyArtifactManifest({ directory: malformed }), { code: 'CANDIDATE_ARTIFACT_MANIFEST_INVALID' });
});
test('SCN-artifact-manifest-drift: post-seal mutation or overwrite is detected and cannot become retained evidence', () => {
const root = fixture();
write(root, 'proof.txt', 'before');
sealArtifactManifest({ directory: root });
write(root, 'proof.txt', 'after');
assert.throws(() => verifyArtifactManifest({ directory: root }), { code: 'CANDIDATE_ARTIFACT_SEAL_DRIFT' });
});
test('SCN-artifact-manifest-bytewise-and-caps: UTF-8 bytewise path ordering and every declared cap are deterministic', () => {
const first = fixture();
const second = fixture();
write(first, 'ä.txt', 'umlaut');
write(first, 'z.txt', 'zed');
write(second, 'z.txt', 'zed');
write(second, 'ä.txt', 'umlaut');
const left = sealArtifactManifest({ directory: first });
const right = sealArtifactManifest({ directory: second });
assert.deepEqual(left.files.map((file) => file.path), ['z.txt', 'ä.txt']);
assert.deepEqual(left.files, right.files);
assert.equal(left.sealDigest, right.sealDigest);
const count = fixture();
write(count, 'one.txt', '1');
write(count, 'two.txt', '2');
assert.throws(() => sealArtifactManifest({ directory: count, limits: { maxFiles: 1 } }), { code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
const countPass = fixture();
write(countPass, 'one.txt', '1');
assert.equal(sealArtifactManifest({ directory: countPass, limits: { maxFiles: 1 } }).files.length, 1);
const perFile = fixture();
write(perFile, 'proof.txt', '1234');
assert.equal(sealArtifactManifest({ directory: perFile, limits: { maxFileBytes: 4 } }).files[0].bytes, 4);
const perFileFail = fixture();
write(perFileFail, 'proof.txt', '12345');
assert.throws(() => sealArtifactManifest({ directory: perFileFail, limits: { maxFileBytes: 4 } }), { code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
const aggregate = fixture();
write(aggregate, 'one.txt', '12');
write(aggregate, 'two.txt', '34');
assert.throws(() => sealArtifactManifest({ directory: aggregate, limits: { maxTotalBytes: 3 } }), { code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
const aggregatePass = fixture();
write(aggregatePass, 'one.txt', '12');
write(aggregatePass, 'two.txt', '34');
assert.equal(sealArtifactManifest({ directory: aggregatePass, limits: { maxTotalBytes: 4 } }).totalBytes, 4);
const paths = fixture();
write(paths, 'abcd.txt', 'x');
assert.throws(() => sealArtifactManifest({ directory: paths, limits: { maxPathBytes: 7 } }), { code: 'CANDIDATE_ARTIFACT_PATH_INVALID' });
const pathsPass = fixture();
write(pathsPass, 'abc.txt', 'x');
assert.equal(sealArtifactManifest({ directory: pathsPass, limits: { maxPathBytes: 7 } }).files.length, 1);
const manifestCap = fixture();
write(manifestCap, 'proof.txt', 'proof');
assert.throws(() => sealArtifactManifest({ directory: manifestCap, limits: { maxManifestBytes: 16 } }), { code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
const exactManifestBytes = manifestBytesFor('proof.txt', 'proof');
const manifestAtCap = fixture();
write(manifestAtCap, 'proof.txt', 'proof');
assert.equal(sealArtifactManifest({ directory: manifestAtCap, limits: { maxManifestBytes: exactManifestBytes } }).files.length, 1);
const manifestOverCap = fixture();
write(manifestOverCap, 'proof.txt', 'proof');
assert.throws(() => sealArtifactManifest({ directory: manifestOverCap, limits: { maxManifestBytes: exactManifestBytes - 1 } }),
{ code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
assert.throws(() => sealArtifactManifest({ directory: fixture(), limits: { unknownCap: 1 } }), { code: 'CANDIDATE_ARTIFACT_LIMIT_INVALID' });
});
test('SCN-artifact-manifest-root-and-manifest-boundaries: root or manifest links, special nodes and oversized manifests never become evidence', () => {
const actual = fixture();
write(actual, 'proof.txt', 'proof');
const linkedRoot = `${actual}-link`;
symlinkSync(actual, linkedRoot);
assert.throws(() => sealArtifactManifest({ directory: linkedRoot }), { code: 'CANDIDATE_ARTIFACT_LINK_REJECTED' });
const sealed = fixture();
write(sealed, 'proof.txt', 'proof');
sealArtifactManifest({ directory: sealed });
const manifest = join(sealed, ARTIFACT_MANIFEST_FILE);
const replacement = `${manifest}.replacement`;
renameSync(manifest, replacement);
symlinkSync(replacement, manifest);
assert.throws(() => verifyArtifactManifest({ directory: sealed }), { code: 'CANDIDATE_ARTIFACT_LINK_REJECTED' });
assert.throws(() => sealArtifactManifest({ directory: sealed }), { code: 'CANDIDATE_ARTIFACT_ALREADY_SEALED' });
const oversized = fixture();
write(oversized, 'proof.txt', 'proof');
sealArtifactManifest({ directory: oversized });
writeFileSync(join(oversized, ARTIFACT_MANIFEST_FILE), 'x'.repeat(80));
assert.throws(() => verifyArtifactManifest({ directory: oversized, limits: { maxManifestBytes: 64 } }),
{ code: 'CANDIDATE_ARTIFACT_LIMIT_EXCEEDED' });
const special = fixture();
write(special, 'proof.txt', 'proof');
sealArtifactManifest({ directory: special });
const specialManifest = join(special, ARTIFACT_MANIFEST_FILE);
unlinkSync(specialManifest);
mkdirSync(specialManifest);
assert.throws(() => verifyArtifactManifest({ directory: special }), { code: 'CANDIDATE_ARTIFACT_SPECIAL_REJECTED' });
});
test('SCN-artifact-manifest-complete-drift: adding artifacts or replacing the manifest cannot preserve a sealed record', () => {
const root = fixture();
write(root, 'proof.txt', 'before');
sealArtifactManifest({ directory: root });
write(root, 'added.txt', 'after');
assert.throws(() => verifyArtifactManifest({ directory: root }), { code: 'CANDIDATE_ARTIFACT_SEAL_DRIFT' });
const replaced = fixture();
write(replaced, 'proof.txt', 'before');
sealArtifactManifest({ directory: replaced });
writeFileSync(join(replaced, ARTIFACT_MANIFEST_FILE), JSON.stringify({ schema: 'torch.dev/artifact-manifest/v1alpha1', files: [], totalBytes: 0, sealDigest: '0'.repeat(64) }));
assert.throws(() => verifyArtifactManifest({ directory: replaced }), { code: 'CANDIDATE_ARTIFACT_SEAL_INVALID' });
});
+186
View File
@@ -0,0 +1,186 @@
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { mkdtempSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import { CANDIDATE_ENGINE_LIMITS, runCandidateEngine } from '../../src/checks/candidate-engine.mjs';
function engineLimits(overrides = {}) {
return { ...CANDIDATE_ENGINE_LIMITS, ...overrides };
}
function fixtureInput() {
const bytes = Buffer.from('fixture input bytes', 'utf8');
return {
directory: mkdtempSync(join(tmpdir(), 'torch-candidate-engine-')),
digest: createHash('sha256').update(bytes).digest('hex'),
bytes,
byteLength: bytes.length,
};
}
function fixtureOptions(overrides = {}) {
return {
input: fixtureInput(),
definition: { command: 'fixture-command', args: ['--fixed'] },
args: ['--bounded'],
environment: { TZ: 'UTC' },
artifactDirectory: mkdtempSync(join(tmpdir(), 'torch-candidate-artifacts-')),
executor: () => ({ status: 0, stdout: 'fixture output', stderr: '' }),
limits: engineLimits(),
...overrides,
};
}
test('SCN-candidate-engine-pure-fixture: immutable bounded fixture execution returns only parent-observed nonpromotable evidence', () => {
let invocation;
const options = fixtureOptions({
executor: (command, args, execution) => {
invocation = { command, args, execution };
return { status: 0, stdout: 'fixture output', stderr: '' };
},
});
const observed = runCandidateEngine(options);
assert.equal(observed.fixtureOnly, true);
assert.deepEqual(observed.sourceAttestation, {
schema: 'torch.dev/source-attestation/v1alpha1', promotion: 'nonpromotable',
});
assert.equal(Object.hasOwn(observed.sourceAttestation, 'receiptId'), false);
assert.equal(observed.terminal.success, true);
assert.equal(observed.terminal.kind, 'exit');
assert.deepEqual(invocation.args, ['--fixed', '--bounded']);
assert.equal(invocation.execution.cwd, options.input.directory);
assert.deepEqual(invocation.execution.env, { TZ: 'UTC' });
assert.equal(Object.isFrozen(invocation.execution), true);
assert.equal(invocation.execution.timeout, CANDIDATE_ENGINE_LIMITS.timeoutMs);
assert.equal(invocation.execution.maxBuffer, CANDIDATE_ENGINE_LIMITS.maxOutputBytes);
assert.equal(Object.hasOwn(invocation.execution, 'token'), false);
assert.equal(Object.hasOwn(invocation.execution, 'receiptWriter'), false);
assert.equal(Object.hasOwn(invocation.execution, 'controlPlane'), false);
});
test('SCN-candidate-engine-limits: invalid bounded inputs, arguments, environment and artifact descriptors refuse before execution', () => {
let executions = 0;
const executor = () => { executions += 1; return { status: 0 }; };
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, input: { directory: '/tmp', digest: 'bad', bytes: Buffer.from('x'), byteLength: 1 } })),
{ code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, args: ['x'.repeat(8)], limits: engineLimits({ maxArgBytes: 4 }) })),
{ code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, environment: { bad: '1' } })),
{ code: 'CANDIDATE_ENGINE_ENVIRONMENT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, artifactDirectory: 'x'.repeat(5), limits: engineLimits({ maxArtifactPathBytes: 4 }) })),
{ code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, limits: engineLimits({ maxInputBytes: 4 }) })),
{ code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, environment: { TZ: 'UTC', LANG: 'C.UTF-8' }, limits: engineLimits({ maxEnvironmentEntries: 1 }) })),
{ code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.equal(executions, 0, 'refusal must occur before a candidate executor starts');
});
test('SCN-candidate-engine-terminal-observation: overflow, including exit zero plus ENOBUFS, is raw parent evidence and never success', () => {
const overflow = runCandidateEngine(fixtureOptions({
limits: engineLimits({ maxOutputBytes: 4 }),
executor: () => ({ status: 0, error: Object.assign(new Error('overflow'), { code: 'ENOBUFS' }), stdout: 'too many bytes' }),
}));
assert.equal(overflow.terminal.exitStatus, 0);
assert.equal(overflow.terminal.kind, 'overflow');
assert.equal(overflow.terminal.success, false);
assert.equal(overflow.terminal.overflow, true);
const timeout = runCandidateEngine(fixtureOptions({
executor: () => ({ status: null, timedOut: true, error: Object.assign(new Error('timeout'), { code: 'ETIMEDOUT' }) }),
}));
assert.equal(timeout.terminal.kind, 'timeout');
assert.equal(timeout.terminal.success, false);
const signal = runCandidateEngine(fixtureOptions({ executor: () => ({ status: null, signal: 'SIGTERM' }) }));
assert.equal(signal.terminal.kind, 'signal');
assert.equal(signal.terminal.success, false);
});
test('SCN-candidate-engine-capability-boundary: registered-state and writer-shaped inputs are rejected before execution', () => {
let executions = 0;
const options = fixtureOptions({ executor: () => { executions += 1; return { status: 0 }; } });
options.controlPlane = {};
assert.throws(() => runCandidateEngine(options), { code: 'CANDIDATE_ENGINE_CAPABILITY_FORBIDDEN' });
assert.equal(executions, 0);
});
test('SCN-candidate-engine-boundary-limits: every execution bound is explicit, finite and supplied to the native-shaped executor options', () => {
let executions = 0;
const executor = () => { executions += 1; return { status: 0 }; };
const missingTimeout = engineLimits();
delete missingTimeout.timeoutMs;
const missingOutput = engineLimits();
delete missingOutput.maxOutputBytes;
const missingInput = engineLimits();
delete missingInput.maxInputBytes;
for (const limits of [missingTimeout, missingOutput, missingInput, engineLimits({ timeoutMs: 0 }), engineLimits({ timeoutMs: 60_001 })]) {
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, limits })), { code: 'CANDIDATE_ENGINE_LIMIT_INVALID' });
}
assert.equal(executions, 0, 'missing or invalid bounds refuse before execution');
let execution;
runCandidateEngine(fixtureOptions({ executor: (_command, _args, options) => { execution = options; return { status: 0 }; } }));
assert.deepEqual(Object.keys(execution).sort(), ['artifactDirectory', 'cwd', 'encoding', 'env', 'killSignal', 'maxBuffer', 'stdio', 'timeout']);
assert.equal(execution.timeout, CANDIDATE_ENGINE_LIMITS.timeoutMs);
assert.equal(execution.maxBuffer, CANDIDATE_ENGINE_LIMITS.maxOutputBytes);
});
test('SCN-candidate-engine-utf8-overflow: retained output is a valid UTF-8 prefix inside the byte cap and cannot become success', () => {
const fourBytes = runCandidateEngine(fixtureOptions({
limits: engineLimits({ maxOutputBytes: 4 }),
executor: () => ({ status: 0, stdout: '😀😀', stderr: '' }),
}));
assert.equal(fourBytes.terminal.stdout.text, '😀');
assert.equal(Buffer.byteLength(fourBytes.terminal.stdout.text, 'utf8'), 4);
assert.equal(fourBytes.terminal.overflow, true);
assert.equal(fourBytes.terminal.success, false);
const threeBytes = runCandidateEngine(fixtureOptions({
limits: engineLimits({ maxOutputBytes: 3 }),
executor: () => ({ status: 0, stdout: '😀😀', stderr: '' }),
}));
assert.equal(threeBytes.terminal.stdout.text, '');
assert.equal(Buffer.byteLength(threeBytes.terminal.stdout.text, 'utf8') <= 3, true);
assert.equal(threeBytes.terminal.stdout.text.includes('\uFFFD'), false);
assert.equal(threeBytes.terminal.success, false);
});
test('SCN-candidate-engine-immutable-input-and-environment: only matched byte snapshots and allowlisted deterministic environment reach the executor', () => {
let executions = 0;
const executor = () => { executions += 1; return { status: 0 }; };
const mismatched = fixtureInput();
mismatched.digest = '0'.repeat(64);
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, input: mismatched })), { code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, input: { directory: '/tmp', digest: '0'.repeat(64), byteLength: 0 } })),
{ code: 'CANDIDATE_ENGINE_INPUT_INVALID' });
assert.throws(() => runCandidateEngine(fixtureOptions({ executor, environment: { UNREVIEWED_SECRET: '1' } })),
{ code: 'CANDIDATE_ENGINE_ENVIRONMENT_INVALID' });
assert.equal(executions, 0, 'invalid snapshot or environment refuses before execution');
const input = fixtureInput();
const definition = { command: 'fixture-command', args: ['--fixed'] };
const args = ['--bounded'];
const environment = { TZ: 'UTC' };
let invocation;
const observed = runCandidateEngine(fixtureOptions({
input, definition, args, environment,
executor: (_command, commandArgs, execution) => {
input.bytes.fill(0);
definition.args[0] = '--mutated';
args[0] = '--mutated';
environment.TZ = 'America/New_York';
invocation = { commandArgs, execution };
return { status: 0 };
},
}));
assert.deepEqual(invocation.commandArgs, ['--fixed', '--bounded']);
assert.deepEqual(invocation.execution.env, { TZ: 'UTC' });
assert.deepEqual(observed.input, {
directory: input.directory,
digest: createHash('sha256').update(Buffer.from('fixture input bytes')).digest('hex'),
bytes: Buffer.byteLength('fixture input bytes'),
});
});