@mettlelabs/mettle 0.0.0-beta.1 → 0.0.0-beta.1-linux-x64
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/THIRD_PARTY_NOTICES.md +11479 -0
- package/bin/mettle +4 -0
- package/harness/audit-investigation.mjs +203 -0
- package/harness/chunk-13tgmdey.mjs +33761 -0
- package/harness/chunk-1sygmp11.mjs +18142 -0
- package/harness/chunk-1ykxpmaz.mjs +7887 -0
- package/harness/chunk-2gt4cy7x.mjs +2290 -0
- package/harness/chunk-2pm2sayw.mjs +248 -0
- package/harness/chunk-32y07kkp.mjs +3747 -0
- package/harness/chunk-3m7jn5ac.mjs +16242 -0
- package/harness/chunk-3wvjs93w.mjs +538 -0
- package/harness/chunk-4bepyxmq.mjs +113 -0
- package/harness/chunk-4jtnt9k5.mjs +95 -0
- package/harness/chunk-50sbrf09.mjs +18972 -0
- package/harness/chunk-56csr8sw.mjs +322 -0
- package/harness/chunk-5cvb1jnb.mjs +41 -0
- package/harness/chunk-5wgmv4np.mjs +585 -0
- package/harness/chunk-65eyq32y.mjs +225 -0
- package/harness/chunk-6fnh6qhk.mjs +195 -0
- package/harness/chunk-6kgqw9ny.mjs +32303 -0
- package/harness/chunk-7b242j0n.mjs +14 -0
- package/harness/chunk-7bg2gnv2.mjs +7540 -0
- package/harness/chunk-ageqxj9v.mjs +63008 -0
- package/harness/chunk-b0qbt5jd.mjs +14 -0
- package/harness/chunk-b5g1r3fz.mjs +4785 -0
- package/harness/chunk-b71gpkxg.mjs +1514 -0
- package/harness/chunk-bdv1rx4a.mjs +20364 -0
- package/harness/chunk-c1631s2x.mjs +12241 -0
- package/harness/chunk-d5xgxsnp.mjs +74 -0
- package/harness/chunk-erg1fbh0.mjs +0 -0
- package/harness/chunk-fh78v9vn.mjs +19366 -0
- package/harness/chunk-fjx29qvp.mjs +50 -0
- package/harness/chunk-gryka6kg.mjs +39 -0
- package/harness/chunk-jb0r19et.mjs +180 -0
- package/harness/chunk-mfn574ss.mjs +179 -0
- package/harness/chunk-msg872ea.mjs +334 -0
- package/harness/chunk-nz5sv9as.mjs +1642 -0
- package/harness/chunk-p9d3cgvq.mjs +452 -0
- package/harness/chunk-pq6wvwxr.mjs +12 -0
- package/harness/chunk-qcm9arf8.mjs +7736 -0
- package/harness/chunk-qr8zfqjd.mjs +24 -0
- package/harness/chunk-rz2tz6jp.mjs +26 -0
- package/harness/chunk-sbkgc3kn.mjs +8 -0
- package/harness/chunk-v66qvpnb.mjs +169 -0
- package/harness/chunk-vjaxzab3.mjs +136 -0
- package/harness/chunk-vr1n5nza.mjs +1045 -0
- package/harness/chunk-vsf2zajv.mjs +3867 -0
- package/harness/chunk-wq253fj4.mjs +19041 -0
- package/harness/chunk-wyv7ceq4.mjs +0 -0
- package/harness/chunk-xnqknk2r.mjs +198 -0
- package/harness/chunk-xqyc4ca2.mjs +1890 -0
- package/harness/chunk-z6n9thea.mjs +45146 -0
- package/harness/chunk-zty0d3bw.mjs +0 -0
- package/harness/chunk-zvkxtj82.mjs +42 -0
- package/harness/issue-api-token.mjs +35 -0
- package/harness/launcher.mjs +159 -0
- package/harness/sandbox.mjs +132 -0
- package/harness/server.mjs +66322 -0
- package/package.json +13 -15
- package/bin/mettle.js +0 -51
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
string,
|
|
3
|
+
number,
|
|
4
|
+
array,
|
|
5
|
+
strictObject,
|
|
6
|
+
_enum
|
|
7
|
+
} from "./chunk-1sygmp11.mjs";
|
|
8
|
+
|
|
9
|
+
// apps/harness/src/access.ts
|
|
10
|
+
var identity = string().min(1).max(256);
|
|
11
|
+
var RunAuthoritySchema = strictObject({
|
|
12
|
+
tenantId: identity,
|
|
13
|
+
actorId: identity,
|
|
14
|
+
engagementId: identity,
|
|
15
|
+
policyRevision: identity
|
|
16
|
+
});
|
|
17
|
+
var ApiPrincipalSchema = RunAuthoritySchema.extend({
|
|
18
|
+
permissions: array(_enum([
|
|
19
|
+
"run:create",
|
|
20
|
+
"engagement:create",
|
|
21
|
+
"run:read",
|
|
22
|
+
"run:control",
|
|
23
|
+
"run:recover",
|
|
24
|
+
"run:delete"
|
|
25
|
+
])).min(1).refine((permissions) => new Set(permissions).size === permissions.length),
|
|
26
|
+
expiresAt: number().int().positive()
|
|
27
|
+
});
|
|
28
|
+
var ApiTokenRecordSchema = strictObject({
|
|
29
|
+
tokenSha256: string().regex(/^[a-f0-9]{64}$/),
|
|
30
|
+
principal: ApiPrincipalSchema
|
|
31
|
+
});
|
|
32
|
+
var BEARER_TOKEN_PATTERN = /^Bearer ([A-Za-z0-9_-]{43})$/;
|
|
33
|
+
function authorityFor(principal) {
|
|
34
|
+
return RunAuthoritySchema.parse({
|
|
35
|
+
tenantId: principal.tenantId,
|
|
36
|
+
actorId: principal.actorId,
|
|
37
|
+
engagementId: principal.engagementId,
|
|
38
|
+
policyRevision: principal.policyRevision
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { RunAuthoritySchema, ApiPrincipalSchema, ApiTokenRecordSchema, BEARER_TOKEN_PATTERN, authorityFor };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import"./chunk-2gt4cy7x.mjs";
|
|
2
|
+
import"./chunk-zvkxtj82.mjs";
|
|
3
|
+
import {
|
|
4
|
+
writeApiTokenFiles
|
|
5
|
+
} from "./chunk-4jtnt9k5.mjs";
|
|
6
|
+
|
|
7
|
+
// apps/harness/scripts/issue-api-token.ts
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { parseArgs } from "node:util";
|
|
10
|
+
try {
|
|
11
|
+
const { values } = parseArgs({
|
|
12
|
+
options: {
|
|
13
|
+
request: { type: "string" },
|
|
14
|
+
"output-dir": { type: "string" },
|
|
15
|
+
help: { type: "boolean" }
|
|
16
|
+
},
|
|
17
|
+
strict: true,
|
|
18
|
+
allowPositionals: false
|
|
19
|
+
});
|
|
20
|
+
if (values.help) {
|
|
21
|
+
process.stdout.write(`Usage: issue-api-token --request <request.json> --output-dir <new-private-directory>
|
|
22
|
+
`);
|
|
23
|
+
} else {
|
|
24
|
+
if (!values.request || !values["output-dir"])
|
|
25
|
+
throw new Error("Explicit request and output directory required.");
|
|
26
|
+
const request = JSON.parse(await readFile(values.request, "utf8"));
|
|
27
|
+
const result = await writeApiTokenFiles(request, values["output-dir"]);
|
|
28
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
process.stderr.write(`API token issuance failed. Check the request, existing parent directory, and unused output path; no credentials printed.
|
|
33
|
+
`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTRACT_VERSION,
|
|
3
|
+
RunConfigSchema,
|
|
4
|
+
HarnessError,
|
|
5
|
+
publicError,
|
|
6
|
+
COORDINATOR_TARGET
|
|
7
|
+
} from "./chunk-2gt4cy7x.mjs";
|
|
8
|
+
import {
|
|
9
|
+
RunAuthoritySchema
|
|
10
|
+
} from "./chunk-zvkxtj82.mjs";
|
|
11
|
+
import {
|
|
12
|
+
SandboxConfigSchema,
|
|
13
|
+
DockerEngine,
|
|
14
|
+
checkSandbox
|
|
15
|
+
} from "./chunk-xqyc4ca2.mjs";
|
|
16
|
+
import {
|
|
17
|
+
liveRoute,
|
|
18
|
+
liveProfiles,
|
|
19
|
+
loadInvestigationGrant,
|
|
20
|
+
SOURCE_TOTAL_BYTES,
|
|
21
|
+
SOURCE_FILES_MAX,
|
|
22
|
+
InvestigationProfilesSchema
|
|
23
|
+
} from "./chunk-32y07kkp.mjs";
|
|
24
|
+
import {
|
|
25
|
+
privateFile,
|
|
26
|
+
ownedDirectory
|
|
27
|
+
} from "./chunk-7bg2gnv2.mjs";
|
|
28
|
+
|
|
29
|
+
// apps/harness/scripts/launcher.ts
|
|
30
|
+
import { parseArgs } from "node:util";
|
|
31
|
+
|
|
32
|
+
// apps/harness/src/runtime/launcher.ts
|
|
33
|
+
import { join } from "node:path";
|
|
34
|
+
function launchTokenRequest() {
|
|
35
|
+
return {
|
|
36
|
+
tenantId: "local-team",
|
|
37
|
+
actorId: "operator",
|
|
38
|
+
engagementId: "counter-development",
|
|
39
|
+
policyRevision: "v1",
|
|
40
|
+
permissions: [
|
|
41
|
+
"run:create",
|
|
42
|
+
"run:read",
|
|
43
|
+
"run:control",
|
|
44
|
+
"run:recover",
|
|
45
|
+
"engagement:create"
|
|
46
|
+
],
|
|
47
|
+
ttlSeconds: 86400
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function launchRunConfig(environment) {
|
|
51
|
+
return RunConfigSchema.parse({
|
|
52
|
+
contractVersion: CONTRACT_VERSION,
|
|
53
|
+
model: liveRoute(environment).model
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function investigationProfiles(setup) {
|
|
57
|
+
const { environment, location = "local", engineId } = setup;
|
|
58
|
+
const { tenantId, actorId, engagementId, policyRevision } = launchTokenRequest();
|
|
59
|
+
const base = {
|
|
60
|
+
authority: setup.authority ?? RunAuthoritySchema.parse({
|
|
61
|
+
tenantId,
|
|
62
|
+
actorId,
|
|
63
|
+
engagementId,
|
|
64
|
+
policyRevision
|
|
65
|
+
}),
|
|
66
|
+
models: liveProfiles(environment).map((model) => ({
|
|
67
|
+
id: model.route.model,
|
|
68
|
+
provider: model.route.provider
|
|
69
|
+
})),
|
|
70
|
+
expiresAt: setup.expiresAt ?? new Date(Date.now() + launchTokenRequest().ttlSeconds * 1000).toISOString(),
|
|
71
|
+
stages: { maxWorkers: 2, huntPartitions: 2 }
|
|
72
|
+
};
|
|
73
|
+
const pinned = async (input) => {
|
|
74
|
+
const config = SandboxConfigSchema.parse(input);
|
|
75
|
+
if (location === "vm" && !engineId)
|
|
76
|
+
throw new HarnessError("investigation_config", "VM setup requires --engine-id from the designated VM, independently of the forwarded socket.");
|
|
77
|
+
const engine = await checkSandbox({ ...config, ...engineId ? { engineId } : {} }, setup.engine ?? new DockerEngine(config.socket));
|
|
78
|
+
return SandboxConfigSchema.parse({ ...config, engineId: engine, location });
|
|
79
|
+
};
|
|
80
|
+
const profiles = [];
|
|
81
|
+
if (setup.review) {
|
|
82
|
+
if (setup.githubTokenFile)
|
|
83
|
+
await privateFile(setup.githubTokenFile, 4096);
|
|
84
|
+
const localRoot = setup.localRoot ? await ownedDirectory(setup.localRoot, new HarnessError("investigation_config", "The local review root must be a directory owned by this operator.")) : undefined;
|
|
85
|
+
profiles.push({
|
|
86
|
+
...base,
|
|
87
|
+
id: `${location}-review`,
|
|
88
|
+
mode: "codereview",
|
|
89
|
+
sandbox: await pinned({
|
|
90
|
+
image: environment.HARNESS_SANDBOX_IMAGE,
|
|
91
|
+
socket: environment.HARNESS_SANDBOX_SOCKET,
|
|
92
|
+
runtime: environment.HARNESS_SANDBOX_RUNTIME
|
|
93
|
+
}),
|
|
94
|
+
workspaceRoot: join(setup.directory, "snapshots"),
|
|
95
|
+
...localRoot ? { localRoot } : {},
|
|
96
|
+
maxFiles: SOURCE_FILES_MAX,
|
|
97
|
+
maxSourceBytes: SOURCE_TOTAL_BYTES,
|
|
98
|
+
allowPatchProposals: true,
|
|
99
|
+
...setup.githubTokenFile ? { githubTokenFile: setup.githubTokenFile } : {}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (setup.pentestGrant) {
|
|
103
|
+
const grant = await loadInvestigationGrant(setup.pentestGrant);
|
|
104
|
+
if (grant.mode !== "pentest")
|
|
105
|
+
throw new HarnessError("investigation_config", "The pentest profile requires a pentest grant.");
|
|
106
|
+
if (location === "vm" && !grant.sandbox)
|
|
107
|
+
throw new HarnessError("investigation_config", "VM profiles require an isolated live target grant.");
|
|
108
|
+
profiles.push({
|
|
109
|
+
...base,
|
|
110
|
+
id: `${location}-pentest`,
|
|
111
|
+
mode: "pentest",
|
|
112
|
+
grant: {
|
|
113
|
+
...grant,
|
|
114
|
+
...grant.sandbox ? { sandbox: await pinned(grant.sandbox) } : {}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return InvestigationProfilesSchema.parse(profiles);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// apps/harness/scripts/launcher.ts
|
|
122
|
+
var USAGE = "Usage: launcher.ts token-request | run-config | investigation-profiles --directory <path> [--github-token-file <path>]";
|
|
123
|
+
try {
|
|
124
|
+
const { positionals, values } = parseArgs({
|
|
125
|
+
strict: true,
|
|
126
|
+
allowPositionals: true,
|
|
127
|
+
options: {
|
|
128
|
+
directory: { type: "string" },
|
|
129
|
+
"github-token-file": { type: "string" }
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const [command] = positionals;
|
|
133
|
+
if (positionals.length !== 1 || command !== "investigation-profiles" && Object.keys(values).length > 0)
|
|
134
|
+
throw new Error(USAGE);
|
|
135
|
+
if (command === "token-request")
|
|
136
|
+
process.stdout.write(`${JSON.stringify(launchTokenRequest())}
|
|
137
|
+
`);
|
|
138
|
+
else if (command === "run-config")
|
|
139
|
+
process.stdout.write(`${JSON.stringify({
|
|
140
|
+
target: COORDINATOR_TARGET,
|
|
141
|
+
config: launchRunConfig(process.env)
|
|
142
|
+
})}
|
|
143
|
+
`);
|
|
144
|
+
else if (command === "investigation-profiles" && values.directory) {
|
|
145
|
+
const profiles = await investigationProfiles({
|
|
146
|
+
environment: process.env,
|
|
147
|
+
directory: values.directory,
|
|
148
|
+
review: true,
|
|
149
|
+
...values["github-token-file"] ? { githubTokenFile: values["github-token-file"] } : {}
|
|
150
|
+
});
|
|
151
|
+
process.stdout.write(`${JSON.stringify(profiles, null, 2)}
|
|
152
|
+
`);
|
|
153
|
+
} else
|
|
154
|
+
throw new Error(USAGE);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
process.stderr.write(`${error instanceof Error && error.message === USAGE ? USAGE : publicError(error)}
|
|
157
|
+
`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HarnessError,
|
|
3
|
+
publicError,
|
|
4
|
+
FIXTURE_TARGET,
|
|
5
|
+
fixtureConfig
|
|
6
|
+
} from "./chunk-2gt4cy7x.mjs";
|
|
7
|
+
import {
|
|
8
|
+
ToolExecutor,
|
|
9
|
+
SandboxProbeJournal,
|
|
10
|
+
SandboxJournal,
|
|
11
|
+
reapSandboxes
|
|
12
|
+
} from "./chunk-msg872ea.mjs";
|
|
13
|
+
import {
|
|
14
|
+
sandboxRuntime,
|
|
15
|
+
PROBE_TOOL,
|
|
16
|
+
ToolRegistry,
|
|
17
|
+
DockerEngine,
|
|
18
|
+
sandboxConfig,
|
|
19
|
+
checkSandbox,
|
|
20
|
+
SandboxWorker
|
|
21
|
+
} from "./chunk-xqyc4ca2.mjs";
|
|
22
|
+
import"./chunk-7bg2gnv2.mjs";
|
|
23
|
+
import"./chunk-zvkxtj82.mjs";
|
|
24
|
+
import {
|
|
25
|
+
databaseConfig
|
|
26
|
+
} from "./chunk-5cvb1jnb.mjs";
|
|
27
|
+
import {
|
|
28
|
+
uuid
|
|
29
|
+
} from "./chunk-1sygmp11.mjs";
|
|
30
|
+
|
|
31
|
+
// apps/harness/src/sandbox/probe.ts
|
|
32
|
+
import { randomUUID } from "node:crypto";
|
|
33
|
+
async function sandboxProbe(args) {
|
|
34
|
+
const [command, argument] = args;
|
|
35
|
+
if (!["check", "run", "cleanup", "reap"].includes(command ?? "") || args.length !== (command === "cleanup" ? 2 : 1) || command === "cleanup" && !uuid().safeParse(argument).success)
|
|
36
|
+
throw new HarnessError("sandbox_command", "Use sandbox check, run, reap, or cleanup <runId>.");
|
|
37
|
+
const config = sandboxConfig();
|
|
38
|
+
const engine = new DockerEngine(config.socket);
|
|
39
|
+
if (command === "check") {
|
|
40
|
+
const workerId = await checkSandbox(config, engine);
|
|
41
|
+
console.log(JSON.stringify({
|
|
42
|
+
configured: true,
|
|
43
|
+
runtime: sandboxRuntime(config),
|
|
44
|
+
workerId,
|
|
45
|
+
image: config.image,
|
|
46
|
+
executed: false,
|
|
47
|
+
productionApproved: false
|
|
48
|
+
}));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const journal = new SandboxProbeJournal(databaseConfig());
|
|
52
|
+
const controller = new AbortController;
|
|
53
|
+
let lease;
|
|
54
|
+
let heartbeat;
|
|
55
|
+
let heartbeatTask;
|
|
56
|
+
const stop = () => controller.abort();
|
|
57
|
+
process.on("SIGINT", stop);
|
|
58
|
+
process.on("SIGTERM", stop);
|
|
59
|
+
try {
|
|
60
|
+
if (command === "reap") {
|
|
61
|
+
const report = await reapSandboxes(config, engine, journal, controller.signal);
|
|
62
|
+
console.log(JSON.stringify(report));
|
|
63
|
+
if (report.interrupted || report.outcomes.some((outcome) => outcome.status === "blocked"))
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (command === "run")
|
|
68
|
+
await checkSandbox(config, engine);
|
|
69
|
+
const runId = argument ?? randomUUID();
|
|
70
|
+
if (command === "run")
|
|
71
|
+
await journal.createRun(runId, FIXTURE_TARGET, fixtureConfig());
|
|
72
|
+
lease = await journal.acquire(runId);
|
|
73
|
+
const owned = lease;
|
|
74
|
+
heartbeat = setInterval(() => {
|
|
75
|
+
if (heartbeatTask)
|
|
76
|
+
return;
|
|
77
|
+
heartbeatTask = journal.heartbeat(owned).then(async () => {
|
|
78
|
+
if ((await journal.read(runId)).stopRequested)
|
|
79
|
+
controller.abort();
|
|
80
|
+
}).catch(() => controller.abort()).finally(() => {
|
|
81
|
+
heartbeatTask = undefined;
|
|
82
|
+
});
|
|
83
|
+
}, 1000);
|
|
84
|
+
const worker = new SandboxWorker(config, engine, new SandboxJournal(journal, lease));
|
|
85
|
+
if (command === "cleanup") {
|
|
86
|
+
const removed = await worker.reap();
|
|
87
|
+
console.log(JSON.stringify({
|
|
88
|
+
runId,
|
|
89
|
+
removed,
|
|
90
|
+
actionReplayed: false,
|
|
91
|
+
actionReconciled: false
|
|
92
|
+
}));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(JSON.stringify({
|
|
96
|
+
runId,
|
|
97
|
+
diagnostic: "offline-sandbox-probe",
|
|
98
|
+
providerCalls: 0
|
|
99
|
+
}));
|
|
100
|
+
const callId = "sandbox-probe-1";
|
|
101
|
+
await journal.beginModel(lease, "sandbox-diagnostic", {
|
|
102
|
+
diagnostic: "offline-sandbox-probe"
|
|
103
|
+
});
|
|
104
|
+
await journal.completeModel(lease, "sandbox-diagnostic", {
|
|
105
|
+
tool_calls: [{ id: callId, name: PROBE_TOOL, args: {} }]
|
|
106
|
+
});
|
|
107
|
+
const executor = new ToolExecutor(journal, lease, controller.signal, new ToolRegistry([worker.tool()]));
|
|
108
|
+
const result = JSON.parse(await executor.execute(callId, PROBE_TOOL, {}));
|
|
109
|
+
await journal.finish(lease, "completed");
|
|
110
|
+
console.log(JSON.stringify({ runId, ...result, productionApproved: false }));
|
|
111
|
+
} finally {
|
|
112
|
+
controller.abort();
|
|
113
|
+
if (heartbeat)
|
|
114
|
+
clearInterval(heartbeat);
|
|
115
|
+
await heartbeatTask;
|
|
116
|
+
if (lease)
|
|
117
|
+
await journal.release(lease).catch(() => {
|
|
118
|
+
return;
|
|
119
|
+
});
|
|
120
|
+
await journal.close();
|
|
121
|
+
process.off("SIGINT", stop);
|
|
122
|
+
process.off("SIGTERM", stop);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// apps/harness/scripts/sandbox.ts
|
|
127
|
+
try {
|
|
128
|
+
await sandboxProbe(process.argv.slice(2));
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(publicError(error));
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
}
|