@deftai/directive-core 0.99.0 → 0.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authz/actions.d.ts +6 -0
- package/dist/authz/actions.js +3 -0
- package/dist/authz/classify.js +241 -30
- package/dist/authz/decompose-apply.d.ts +81 -0
- package/dist/authz/decompose-apply.js +377 -0
- package/dist/authz/index.d.ts +3 -1
- package/dist/authz/index.js +3 -1
- package/dist/authz/store.d.ts +53 -0
- package/dist/authz/store.js +219 -1
- package/dist/authz/types.d.ts +19 -2
- package/dist/authz/types.js +5 -1
- package/dist/check/cached-orchestrator.d.ts +26 -5
- package/dist/check/cached-orchestrator.js +228 -6
- package/dist/check/gate-lists.d.ts +10 -4
- package/dist/check/gate-lists.js +19 -5
- package/dist/check/index.d.ts +3 -2
- package/dist/check/index.js +2 -1
- package/dist/check/named-cause.d.ts +45 -0
- package/dist/check/named-cause.js +121 -0
- package/dist/check/orchestrator.js +6 -1
- package/dist/doctor/checks.d.ts +44 -0
- package/dist/doctor/checks.js +428 -2
- package/dist/doctor/help.d.ts +5 -0
- package/dist/doctor/help.js +31 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/main.d.ts +6 -7
- package/dist/doctor/main.js +130 -79
- package/dist/doctor/session-coda.d.ts +82 -0
- package/dist/doctor/session-coda.js +151 -0
- package/dist/doctor/types.d.ts +16 -0
- package/dist/doctor/which.d.ts +20 -1
- package/dist/doctor/which.js +67 -1
- package/dist/finish-loop/pr-finish-loop.d.ts +13 -2
- package/dist/finish-loop/pr-finish-loop.js +116 -5
- package/dist/finish-loop/types.d.ts +1 -1
- package/dist/hooks/dispatcher.d.ts +9 -5
- package/dist/hooks/dispatcher.js +23 -15
- package/dist/hooks/readonly.d.ts +6 -3
- package/dist/hooks/readonly.js +44 -25
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/agent-hooks.d.ts +20 -0
- package/dist/init-deposit/agent-hooks.js +51 -31
- package/dist/init-deposit/gitignore.js +2 -0
- package/dist/intake/issue-ingest.js +36 -0
- package/dist/lifecycle/completed-consistency.d.ts +60 -0
- package/dist/lifecycle/completed-consistency.js +443 -0
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +47 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +415 -0
- package/dist/lifecycle/index.d.ts +2 -0
- package/dist/lifecycle/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +52 -0
- package/dist/literal-acceptance/capture.js +658 -0
- package/dist/literal-acceptance/evaluate.d.ts +47 -0
- package/dist/literal-acceptance/evaluate.js +193 -0
- package/dist/literal-acceptance/index.d.ts +12 -0
- package/dist/literal-acceptance/index.js +12 -0
- package/dist/literal-acceptance/run.d.ts +38 -0
- package/dist/literal-acceptance/run.js +216 -0
- package/dist/literal-acceptance/safety.d.ts +24 -0
- package/dist/literal-acceptance/safety.js +251 -0
- package/dist/literal-acceptance/types.d.ts +82 -0
- package/dist/literal-acceptance/types.js +26 -0
- package/dist/policy/ac-pass-banking.d.ts +58 -0
- package/dist/policy/ac-pass-banking.js +150 -0
- package/dist/policy/ceremony-dial.d.ts +96 -11
- package/dist/policy/ceremony-dial.js +250 -23
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +16 -1
- package/dist/policy/merge-approval-head.d.ts +129 -0
- package/dist/policy/merge-approval-head.js +461 -0
- package/dist/policy/require-human-merge.d.ts +5 -0
- package/dist/policy/require-human-merge.js +5 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +20 -3
- package/dist/pr-merge-readiness/ci-gate.js +65 -5
- package/dist/pr-merge-readiness/compute.d.ts +13 -0
- package/dist/pr-merge-readiness/compute.js +99 -14
- package/dist/pr-merge-readiness/gh.d.ts +49 -0
- package/dist/pr-merge-readiness/gh.js +268 -0
- package/dist/pr-merge-readiness/index.d.ts +2 -2
- package/dist/pr-merge-readiness/index.js +2 -2
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +4 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +11 -0
- package/dist/pr-wait-mergeable/cascade.js +115 -1
- package/dist/pr-wait-mergeable/main.d.ts +4 -0
- package/dist/pr-wait-mergeable/main.js +4 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -1
- package/dist/pr-wait-mergeable/wrappers.js +7 -1
- package/dist/preflight/evaluate.d.ts +15 -1
- package/dist/preflight/evaluate.js +33 -2
- package/dist/preflight-cache/evaluate.d.ts +3 -0
- package/dist/preflight-cache/evaluate.js +3 -0
- package/dist/product-first-done-gate/acceptance.d.ts +25 -0
- package/dist/product-first-done-gate/acceptance.js +247 -0
- package/dist/product-first-done-gate/check-mode.d.ts +45 -0
- package/dist/product-first-done-gate/check-mode.js +148 -0
- package/dist/product-first-done-gate/evaluate.d.ts +50 -0
- package/dist/product-first-done-gate/evaluate.js +255 -0
- package/dist/product-first-done-gate/index.d.ts +11 -0
- package/dist/product-first-done-gate/index.js +11 -0
- package/dist/product-first-done-gate/types.d.ts +64 -0
- package/dist/product-first-done-gate/types.js +61 -0
- package/dist/render/framework-commands.js +4 -0
- package/dist/run-summary/emit.d.ts +56 -0
- package/dist/run-summary/emit.js +171 -0
- package/dist/run-summary/index.d.ts +4 -0
- package/dist/run-summary/index.js +4 -0
- package/dist/run-summary/path.d.ts +25 -0
- package/dist/run-summary/path.js +78 -0
- package/dist/run-summary/types.d.ts +76 -0
- package/dist/run-summary/types.js +21 -0
- package/dist/scope/acceptance-evidence.d.ts +110 -0
- package/dist/scope/acceptance-evidence.js +422 -0
- package/dist/scope/coverage-map.d.ts +98 -0
- package/dist/scope/coverage-map.js +558 -0
- package/dist/scope/decompose.d.ts +5 -0
- package/dist/scope/decompose.js +134 -11
- package/dist/scope/index.d.ts +3 -0
- package/dist/scope/index.js +3 -0
- package/dist/scope/parent-lineage.d.ts +125 -0
- package/dist/scope/parent-lineage.js +626 -0
- package/dist/scope/transition.d.ts +8 -0
- package/dist/scope/transition.js +61 -2
- package/dist/session/ac-pass-banking.d.ts +221 -0
- package/dist/session/ac-pass-banking.js +761 -0
- package/dist/session/active-cli.d.ts +79 -0
- package/dist/session/active-cli.js +382 -0
- package/dist/session/deposit-sha.d.ts +49 -0
- package/dist/session/deposit-sha.js +121 -0
- package/dist/session/effort-budget.d.ts +136 -0
- package/dist/session/effort-budget.js +352 -0
- package/dist/session/index.d.ts +7 -0
- package/dist/session/index.js +7 -0
- package/dist/session/orientation-compression.d.ts +127 -0
- package/dist/session/orientation-compression.js +425 -0
- package/dist/session/orientation-state.d.ts +29 -0
- package/dist/session/orientation-state.js +85 -0
- package/dist/session/session-start.d.ts +31 -0
- package/dist/session/session-start.js +228 -2
- package/dist/session/toolchain-preflight.d.ts +64 -0
- package/dist/session/toolchain-preflight.js +151 -0
- package/dist/session/verify-session-ritual.d.ts +14 -0
- package/dist/session/verify-session-ritual.js +33 -0
- package/dist/story-ready/evaluate.d.ts +11 -0
- package/dist/story-ready/evaluate.js +41 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -2
- package/dist/triage/actions/candidates-log.js +37 -6
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +8 -2
- package/dist/triage/bootstrap/index.js +15 -3
- package/dist/triage/classify/index.d.ts +1 -0
- package/dist/triage/classify/index.js +2 -0
- package/dist/triage/classify/label-mirror.js +16 -0
- package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
- package/dist/triage/classify/mirror-discovery-tip.js +234 -0
- package/dist/triage/reconcile/reconcile.js +28 -6
- package/dist/triage/summary/index.js +12 -4
- package/dist/triage/welcome/default-mode.js +16 -0
- package/dist/ts-check-lane/run-lane.js +21 -4
- package/dist/vbrief-validate/conformance.js +7 -0
- package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
- package/dist/xbrief-migrate/migrate-project.d.ts +4 -0
- package/dist/xbrief-migrate/migrate-project.js +92 -3
- package/package.json +15 -3
package/dist/authz/actions.d.ts
CHANGED
|
@@ -39,6 +39,12 @@ export interface MintGrantInput {
|
|
|
39
39
|
readonly now?: Date;
|
|
40
40
|
/** When true, pin grant id into state.activeGrantIds. */
|
|
41
41
|
readonly pinActive?: boolean;
|
|
42
|
+
/** SHA-256 hex of exact draft bytes for scope.decompose.apply.structural (#3239). */
|
|
43
|
+
readonly contentDigest?: string | null;
|
|
44
|
+
/** Project-relative parent artifact path (#3239). */
|
|
45
|
+
readonly parentPath?: string | null;
|
|
46
|
+
/** Project-relative draft/target path (#3239). */
|
|
47
|
+
readonly targetPath?: string | null;
|
|
42
48
|
}
|
|
43
49
|
export declare function mintHumanOriginGrant(input: MintGrantInput): HumanOriginGrant;
|
|
44
50
|
export interface RevokeGrantInput {
|
package/dist/authz/actions.js
CHANGED
|
@@ -78,6 +78,9 @@ export function mintHumanOriginGrant(input) {
|
|
|
78
78
|
storyIds: input.storyIds ?? [],
|
|
79
79
|
issueIds: input.issueIds ?? [],
|
|
80
80
|
cohortId: input.cohortId ?? null,
|
|
81
|
+
contentDigest: input.contentDigest ?? null,
|
|
82
|
+
parentPath: input.parentPath ?? null,
|
|
83
|
+
targetPath: input.targetPath ?? null,
|
|
81
84
|
},
|
|
82
85
|
semantics: {
|
|
83
86
|
expiresAt: input.expiresAt ?? null,
|
package/dist/authz/classify.js
CHANGED
|
@@ -180,7 +180,7 @@ const POLICY_AUTHORITY_MUTATORS = new Set([
|
|
|
180
180
|
const KILL_SWITCH_BASENAMES = [".deft-directive-disable", ".no-deft-directive"];
|
|
181
181
|
/**
|
|
182
182
|
* Downloaders / decoders / remote-copy tools that can plant files without shell
|
|
183
|
-
* redirects (#3206 / #3213). Not in INDIRECT_WRITE_BINS: those feed hasWriteShape
|
|
183
|
+
* redirects (#3206 / #3213 / #3245). Not in INDIRECT_WRITE_BINS: those feed hasWriteShape
|
|
184
184
|
* and would classify bare `curl $URL` as a store write via opaque-expansion heuristics.
|
|
185
185
|
*/
|
|
186
186
|
const DOWNLOADER_DECODER_BINS = new Set([
|
|
@@ -192,11 +192,103 @@ const DOWNLOADER_DECODER_BINS = new Set([
|
|
|
192
192
|
"scp",
|
|
193
193
|
"aria2c",
|
|
194
194
|
"certutil",
|
|
195
|
+
// #3245 residual after #3213: archive extractors + further alt downloaders.
|
|
196
|
+
"tar",
|
|
197
|
+
"bsdtar",
|
|
198
|
+
"unzip",
|
|
199
|
+
"7z",
|
|
200
|
+
"7za",
|
|
201
|
+
"7zr",
|
|
202
|
+
"rclone",
|
|
203
|
+
"axel",
|
|
204
|
+
"fetch",
|
|
205
|
+
"socat",
|
|
206
|
+
"lftp",
|
|
207
|
+
// #3288 residual after #3245: crypto / alt-download / pipe / archive bins.
|
|
208
|
+
"gpg",
|
|
209
|
+
"age",
|
|
210
|
+
"zstd",
|
|
211
|
+
"unzstd",
|
|
212
|
+
"sftp",
|
|
213
|
+
"wget2",
|
|
214
|
+
"http",
|
|
215
|
+
"https",
|
|
216
|
+
"yt-dlp",
|
|
217
|
+
"ytdlp",
|
|
218
|
+
"aria2",
|
|
219
|
+
"mbuffer",
|
|
220
|
+
"cpio",
|
|
221
|
+
]);
|
|
222
|
+
/**
|
|
223
|
+
* Archive extractors / alt writers that can plant via pathish operands without
|
|
224
|
+
* shell redirects (#3245 / #3288). Used for pathish authz/kill scans (prefer deny)
|
|
225
|
+
* and write-shape residual under UAT — not bare curl-class URL fetches.
|
|
226
|
+
*/
|
|
227
|
+
const ARCHIVE_ALT_WRITE_BINS = new Set([
|
|
228
|
+
"tar",
|
|
229
|
+
"bsdtar",
|
|
230
|
+
"unzip",
|
|
231
|
+
"7z",
|
|
232
|
+
"7za",
|
|
233
|
+
"7zr",
|
|
234
|
+
"rclone",
|
|
235
|
+
"axel",
|
|
236
|
+
"fetch",
|
|
237
|
+
"socat",
|
|
238
|
+
"lftp",
|
|
239
|
+
// #3288 residual after #3245.
|
|
240
|
+
"gpg",
|
|
241
|
+
"age",
|
|
242
|
+
"zstd",
|
|
243
|
+
"unzstd",
|
|
244
|
+
"sftp",
|
|
245
|
+
"wget2",
|
|
246
|
+
"http",
|
|
247
|
+
"https",
|
|
248
|
+
"yt-dlp",
|
|
249
|
+
"ytdlp",
|
|
250
|
+
"aria2",
|
|
251
|
+
"mbuffer",
|
|
252
|
+
"cpio",
|
|
253
|
+
]);
|
|
254
|
+
/**
|
|
255
|
+
* Bins whose pathish operands are scanned for authz/kill destinations (#3213 / #3245 / #3288).
|
|
256
|
+
* Prefer a Set over a long `||` chain so coverage counts one membership check, not N branches.
|
|
257
|
+
*/
|
|
258
|
+
const PROTECTED_POSITIONAL_BINS = new Set([
|
|
259
|
+
"scp",
|
|
260
|
+
"certutil",
|
|
261
|
+
"rclone",
|
|
262
|
+
"tar",
|
|
263
|
+
"bsdtar",
|
|
264
|
+
"unzip",
|
|
265
|
+
"7z",
|
|
266
|
+
"7za",
|
|
267
|
+
"7zr",
|
|
268
|
+
"socat",
|
|
269
|
+
"lftp",
|
|
270
|
+
// #3288 residual.
|
|
271
|
+
"sftp",
|
|
272
|
+
"cpio",
|
|
273
|
+
"gpg",
|
|
274
|
+
"age",
|
|
275
|
+
"zstd",
|
|
276
|
+
"unzstd",
|
|
277
|
+
"mbuffer",
|
|
195
278
|
]);
|
|
279
|
+
/** wget family (directory-prefix dest flags). */
|
|
280
|
+
const WGET_FAMILY_BINS = new Set(["wget", "wget2"]);
|
|
281
|
+
/** aria2 family (dir dest flags). */
|
|
282
|
+
const ARIA2_FAMILY_BINS = new Set(["aria2c", "aria2"]);
|
|
283
|
+
/** 7z family (attached -oDIR only). */
|
|
284
|
+
const SEVEN_Z_FAMILY_BINS = new Set(["7z", "7za", "7zr"]);
|
|
285
|
+
/** tar family (chdir -C / --directory). */
|
|
286
|
+
const TAR_FAMILY_BINS = new Set(["tar", "bsdtar"]);
|
|
196
287
|
/**
|
|
197
288
|
* File destination flags for downloaders/decoders (#3206).
|
|
198
289
|
* normalizeToken lowercases, so wget `-O` and curl `-o` share `-o`.
|
|
199
290
|
* scp: `-o` is an SSH option — excluded in isDownloaderDestFlag.
|
|
291
|
+
* 7z: `-oDIR` is attached-only (no separate value token).
|
|
200
292
|
*/
|
|
201
293
|
const DOWNLOADER_FILE_DEST_FLAGS = new Set([
|
|
202
294
|
"-o",
|
|
@@ -209,11 +301,35 @@ const DOWNLOADER_FILE_DEST_FLAGS = new Set([
|
|
|
209
301
|
const CURL_DIR_DEST_FLAGS = new Set(["--output-dir"]);
|
|
210
302
|
const WGET_DIR_DEST_FLAGS = new Set(["-p", "--directory-prefix"]);
|
|
211
303
|
const ARIA2C_DIR_DEST_FLAGS = new Set(["-d", "--dir"]);
|
|
304
|
+
/**
|
|
305
|
+
* tar/bsdtar extract directory (#3245).
|
|
306
|
+
* Case-sensitive: POSIX tar uses capital `-C` for chdir; lower `-c` is create and is NOT dest.
|
|
307
|
+
*/
|
|
308
|
+
const TAR_DIR_DEST_FLAGS_EXACT = new Set(["-C"]);
|
|
309
|
+
const TAR_DIR_DEST_FLAGS_LOWER = new Set(["--directory"]);
|
|
310
|
+
/** unzip extract directory (#3245). */
|
|
311
|
+
const UNZIP_DIR_DEST_FLAGS = new Set(["-d"]);
|
|
312
|
+
/**
|
|
313
|
+
* cpio chdir before extract/create (#3288).
|
|
314
|
+
* Case-sensitive short form: POSIX cpio uses capital `-D`; lower `-d` is a create option bit.
|
|
315
|
+
* Long form `--directory` is accepted lowercased.
|
|
316
|
+
*/
|
|
317
|
+
const CPIO_DIR_DEST_FLAGS_EXACT = new Set(["-D"]);
|
|
318
|
+
const CPIO_DIR_DEST_FLAGS_LOWER = new Set(["--directory"]);
|
|
212
319
|
/**
|
|
213
320
|
* Symlink / hard-link plant bins (#3213). Absent from prior killWriteBins →
|
|
214
321
|
* `ln -sf … .deft-directive-disable` classified empty → UAT fail-open.
|
|
215
322
|
*/
|
|
216
323
|
const SYMLINK_PLANT_BINS = new Set(["ln", "link", "mklink"]);
|
|
324
|
+
/** socat address prefixes that open a write target (#3245). */
|
|
325
|
+
const SOCAT_WRITE_ADDR_PREFIXES = [
|
|
326
|
+
"open:",
|
|
327
|
+
"create:",
|
|
328
|
+
"creat:",
|
|
329
|
+
"append:",
|
|
330
|
+
"owronly:",
|
|
331
|
+
"oappend:",
|
|
332
|
+
];
|
|
217
333
|
/** Final path segment of a token (path-qualified bins / .exe). */
|
|
218
334
|
function binBareName(token) {
|
|
219
335
|
const pathish = token.replace(/['"]/g, "").toLowerCase().replace(/\\/g, "/");
|
|
@@ -227,17 +343,47 @@ function isDownloaderDecoderBin(token) {
|
|
|
227
343
|
return false;
|
|
228
344
|
return DOWNLOADER_DECODER_BINS.has(binBareName(token));
|
|
229
345
|
}
|
|
230
|
-
|
|
346
|
+
/**
|
|
347
|
+
* True when `flag` (normalized lower) or `rawFlag` (original token) is a dest flag for `bin`.
|
|
348
|
+
* tar `-C` must use raw case: lowercased `-c` is create-archive, not chdir (#3245).
|
|
349
|
+
* 7z uses attached `-oDIR` only — separate `-o PATH` is not a 7z dest form (handled elsewhere).
|
|
350
|
+
*/
|
|
351
|
+
function isDownloaderDestFlag(flag, bin, rawFlag) {
|
|
231
352
|
// scp: `-o` is OpenSSH option (ProxyCommand, …), not a file dest flag.
|
|
232
353
|
if (bin === "scp")
|
|
233
354
|
return false;
|
|
355
|
+
// 7z family: only attached `-oDIR` (parsed in attached-short branch), not separate `-o PATH`.
|
|
356
|
+
if (SEVEN_Z_FAMILY_BINS.has(bin)) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
// #3288: cpio `-o` is copy-out (create archive), not a file dest; dest is `-D` / `--directory`.
|
|
360
|
+
if (bin === "cpio") {
|
|
361
|
+
if (rawFlag !== undefined && CPIO_DIR_DEST_FLAGS_EXACT.has(rawFlag))
|
|
362
|
+
return true;
|
|
363
|
+
// Long form is case-insensitive via normalizeToken (`flag` already lowercased).
|
|
364
|
+
if (CPIO_DIR_DEST_FLAGS_LOWER.has(flag))
|
|
365
|
+
return true;
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
234
368
|
if (DOWNLOADER_FILE_DEST_FLAGS.has(flag))
|
|
235
369
|
return true;
|
|
236
370
|
if (bin === "curl" && CURL_DIR_DEST_FLAGS.has(flag))
|
|
237
371
|
return true;
|
|
238
|
-
if (bin
|
|
372
|
+
if (WGET_FAMILY_BINS.has(bin) && WGET_DIR_DEST_FLAGS.has(flag))
|
|
239
373
|
return true;
|
|
240
|
-
if (bin
|
|
374
|
+
if (ARIA2_FAMILY_BINS.has(bin) && ARIA2C_DIR_DEST_FLAGS.has(flag))
|
|
375
|
+
return true;
|
|
376
|
+
if (TAR_FAMILY_BINS.has(bin)) {
|
|
377
|
+
if (rawFlag !== undefined && TAR_DIR_DEST_FLAGS_EXACT.has(rawFlag))
|
|
378
|
+
return true;
|
|
379
|
+
if (TAR_DIR_DEST_FLAGS_LOWER.has(flag))
|
|
380
|
+
return true;
|
|
381
|
+
// Exact match on raw when normalize collapsed case for long flags only.
|
|
382
|
+
if (rawFlag !== undefined && TAR_DIR_DEST_FLAGS_LOWER.has(normalizeToken(rawFlag)))
|
|
383
|
+
return true;
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (bin === "unzip" && UNZIP_DIR_DEST_FLAGS.has(flag))
|
|
241
387
|
return true;
|
|
242
388
|
return false;
|
|
243
389
|
}
|
|
@@ -292,7 +438,8 @@ function firstUnquotedShellOpIndex(raw) {
|
|
|
292
438
|
/**
|
|
293
439
|
* Destinations from curl/wget/xxd/openssl/scp/aria2c/certutil via -o/--output/-O/-out/
|
|
294
440
|
* --output-dir/-P/-d/--dir (separate, =value, or attached short form), xxd -r path-like
|
|
295
|
-
* write positionals (#3206),
|
|
441
|
+
* write positionals (#3206), positional dests for scp/certutil (#3213), and #3245 residual
|
|
442
|
+
* archive/alt bins (tar -C, unzip -d, 7z -oDIR, rclone positionals, socat OPEN:, axel/fetch -o).
|
|
296
443
|
* openssl uses flags only (no positional dests — avoids treating -in paths as writes).
|
|
297
444
|
* Segment stops at shell operators so compound `scp …authz…; echo` cannot overwrite dests.
|
|
298
445
|
* O(n) token walk — no nested-quantifier regex on untrusted input.
|
|
@@ -309,13 +456,14 @@ function downloaderDecoderDestinations(tokens) {
|
|
|
309
456
|
i++;
|
|
310
457
|
// xxd reverse mode writes; without -r, path positionals are dump inputs (read).
|
|
311
458
|
let xxdReverse = false;
|
|
312
|
-
// scp / certutil: collect pathish operands in this segment
|
|
459
|
+
// scp / certutil / rclone / archive extractors: collect pathish operands in this segment.
|
|
313
460
|
// Fail-closed under UAT: any pathish mentioning protected store/kill paths is a dest
|
|
314
461
|
// candidate (scp source-or-dest of `.deft/authz` is treated as settings — prefer deny
|
|
315
462
|
// over source/dest thrash). Last pathish remains the ordinary write dest.
|
|
316
463
|
// Segment breaks (`;`/`\n`/glued ops) prevent following-command overwrite.
|
|
317
464
|
let lastPositionalPath = null;
|
|
318
465
|
const protectedPathish = [];
|
|
466
|
+
const collectsProtectedPositionals = PROTECTED_POSITIONAL_BINS.has(bin);
|
|
319
467
|
while (i < tokens.length) {
|
|
320
468
|
const raw = tokens[i];
|
|
321
469
|
const n = normalizeToken(raw);
|
|
@@ -361,10 +509,12 @@ function downloaderDecoderDestinations(tokens) {
|
|
|
361
509
|
continue;
|
|
362
510
|
}
|
|
363
511
|
// --flag=value (and rare -out=value); scp -oOption=Value attached form also lands here.
|
|
512
|
+
// tar --directory=PATH also lands here.
|
|
364
513
|
if (n.includes("=") && (n.startsWith("-") || n.startsWith("--"))) {
|
|
365
514
|
const eq = raw.indexOf("=");
|
|
366
|
-
const
|
|
367
|
-
|
|
515
|
+
const flagRaw = raw.slice(0, eq);
|
|
516
|
+
const flag = normalizeToken(flagRaw);
|
|
517
|
+
if (isDownloaderDestFlag(flag, bin, flagRaw)) {
|
|
368
518
|
dests.push(pathishToken(raw.slice(eq + 1)));
|
|
369
519
|
}
|
|
370
520
|
// scp attached -oProxyCommand=… is not a file dest — skip without recording.
|
|
@@ -373,32 +523,55 @@ function downloaderDecoderDestinations(tokens) {
|
|
|
373
523
|
}
|
|
374
524
|
// Attached short: -oPATH / -OPATH (after lowercasing both are -opath…)
|
|
375
525
|
// Skip for scp (OpenSSH -oOption=Value attached forms are not file dests).
|
|
526
|
+
// 7z requires attached -oDIR (#3245).
|
|
376
527
|
if (bin !== "scp" && n.startsWith("-") && !n.startsWith("--") && n.length > 2) {
|
|
377
528
|
if (n.startsWith("-out") && n.length > 4 && !n.startsWith("-output")) {
|
|
378
529
|
dests.push(pathishToken(raw.slice(4)));
|
|
379
530
|
i++;
|
|
380
531
|
continue;
|
|
381
532
|
}
|
|
382
|
-
|
|
533
|
+
// cpio `-o` is copy-out create mode, not a file dest (#3288).
|
|
534
|
+
if (bin !== "cpio" && n.startsWith("-o") && !n.startsWith("-out") && n.length > 2) {
|
|
535
|
+
dests.push(pathishToken(raw.slice(2)));
|
|
536
|
+
i++;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
// wget / wget2 attached -Pdir
|
|
540
|
+
if (WGET_FAMILY_BINS.has(bin) &&
|
|
541
|
+
n.startsWith("-p") &&
|
|
542
|
+
n.length > 2 &&
|
|
543
|
+
!n.startsWith("-proxy")) {
|
|
544
|
+
dests.push(pathishToken(raw.slice(2)));
|
|
545
|
+
i++;
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
// aria2c / aria2 attached -dDIR (#3213 / #3288)
|
|
549
|
+
if (ARIA2_FAMILY_BINS.has(bin) && n.startsWith("-d") && n.length > 2) {
|
|
550
|
+
dests.push(pathishToken(raw.slice(2)));
|
|
551
|
+
i++;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
// unzip attached -dDIR (#3245)
|
|
555
|
+
if (bin === "unzip" && n.startsWith("-d") && n.length > 2) {
|
|
383
556
|
dests.push(pathishToken(raw.slice(2)));
|
|
384
557
|
i++;
|
|
385
558
|
continue;
|
|
386
559
|
}
|
|
387
|
-
//
|
|
388
|
-
if (bin
|
|
560
|
+
// tar attached -CDIR (rare; capital C required)
|
|
561
|
+
if (TAR_FAMILY_BINS.has(bin) && raw.startsWith("-C") && raw.length > 2) {
|
|
389
562
|
dests.push(pathishToken(raw.slice(2)));
|
|
390
563
|
i++;
|
|
391
564
|
continue;
|
|
392
565
|
}
|
|
393
|
-
//
|
|
394
|
-
if (bin === "
|
|
566
|
+
// cpio attached -DDIR (#3288; capital D required)
|
|
567
|
+
if (bin === "cpio" && raw.startsWith("-D") && raw.length > 2) {
|
|
395
568
|
dests.push(pathishToken(raw.slice(2)));
|
|
396
569
|
i++;
|
|
397
570
|
continue;
|
|
398
571
|
}
|
|
399
572
|
}
|
|
400
|
-
// Separate value: -o PATH / --output PATH / -out PATH / --output-dir / -P / -d
|
|
401
|
-
if (isDownloaderDestFlag(n, bin)) {
|
|
573
|
+
// Separate value: -o PATH / --output PATH / -out PATH / --output-dir / -P / -d / -C DIR
|
|
574
|
+
if (isDownloaderDestFlag(n, bin, raw)) {
|
|
402
575
|
const next = tokens[i + 1];
|
|
403
576
|
if (next !== undefined && !String(next).startsWith("-") && !isShellSegmentBreak(next)) {
|
|
404
577
|
dests.push(pathishToken(next));
|
|
@@ -418,16 +591,32 @@ function downloaderDecoderDestinations(tokens) {
|
|
|
418
591
|
dests.push(p);
|
|
419
592
|
}
|
|
420
593
|
}
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
594
|
+
// socat write address forms: OPEN:path, CREATE:path, APPEND:path (#3245).
|
|
595
|
+
if (bin === "socat" && !n.startsWith("-")) {
|
|
596
|
+
const p = pathishToken(raw);
|
|
597
|
+
for (const prefix of SOCAT_WRITE_ADDR_PREFIXES) {
|
|
598
|
+
if (p.startsWith(prefix) && p.length > prefix.length) {
|
|
599
|
+
const dest = p.slice(prefix.length);
|
|
600
|
+
dests.push(dest);
|
|
601
|
+
if (dest.includes(".deft/authz") ||
|
|
602
|
+
dest.includes(".deft-directive-disable") ||
|
|
603
|
+
dest.includes(".no-deft-directive")) {
|
|
604
|
+
protectedPathish.push(dest);
|
|
605
|
+
}
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// scp / certutil / rclone / archive extractors: pathish operands (quote-aware glued-op cut).
|
|
611
|
+
// Under UAT: any `.deft/authz` / kill-switch pathish is fail-closed settings
|
|
612
|
+
// (read vs write thrash deferred — prefer deny over dest-parser perfection; #3213 / #3245).
|
|
613
|
+
if (collectsProtectedPositionals && !n.startsWith("-")) {
|
|
425
614
|
const cut = firstUnquotedShellOpIndex(raw);
|
|
426
615
|
const cleaned = cut >= 0 ? raw.slice(0, cut) : raw;
|
|
427
616
|
const p = pathishToken(cleaned);
|
|
428
617
|
if (p.length > 0) {
|
|
429
618
|
lastPositionalPath = p;
|
|
430
|
-
// Fail-closed: protected store/kill basenames anywhere in
|
|
619
|
+
// Fail-closed: protected store/kill basenames anywhere in pathish.
|
|
431
620
|
if (p.includes(".deft/authz") ||
|
|
432
621
|
p.includes(".deft-directive-disable") ||
|
|
433
622
|
p.includes(".no-deft-directive")) {
|
|
@@ -589,10 +778,12 @@ function hasKillSwitchShellWrite(command, tokens) {
|
|
|
589
778
|
if (pathishMentionsKillSwitch(dest))
|
|
590
779
|
return true;
|
|
591
780
|
}
|
|
592
|
-
// Write/destructive + symlink plant bins with a kill-switch path
|
|
781
|
+
// Write/destructive + symlink plant + archive/alt-write bins with a kill-switch path
|
|
782
|
+
// argument (#3213 ln/link/mklink; #3245 tar/axel/rclone/socat residual).
|
|
593
783
|
const killWriteBins = new Set([
|
|
594
784
|
...INDIRECT_WRITE_BINS,
|
|
595
785
|
...SYMLINK_PLANT_BINS,
|
|
786
|
+
...ARCHIVE_ALT_WRITE_BINS,
|
|
596
787
|
"touch",
|
|
597
788
|
"new-item",
|
|
598
789
|
"ni",
|
|
@@ -606,8 +797,15 @@ function hasKillSwitchShellWrite(command, tokens) {
|
|
|
606
797
|
if (!killWriteBins.has(binTok) && !killWriteBins.has(bare))
|
|
607
798
|
continue;
|
|
608
799
|
for (let tj = ti + 1; tj < tokens.length; tj++) {
|
|
609
|
-
|
|
800
|
+
const p = pathishToken(tokens[tj]);
|
|
801
|
+
if (pathishMentionsKillSwitch(p))
|
|
610
802
|
return true;
|
|
803
|
+
// socat OPEN:.deft-directive-disable — path after address prefix (#3245).
|
|
804
|
+
for (const prefix of SOCAT_WRITE_ADDR_PREFIXES) {
|
|
805
|
+
if (p.startsWith(prefix) && pathishMentionsKillSwitch(p.slice(prefix.length))) {
|
|
806
|
+
return true;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
611
809
|
}
|
|
612
810
|
}
|
|
613
811
|
// Bare `touch .deft-directive-disable` / `ln -sf x .deft-directive-disable` — path later.
|
|
@@ -833,24 +1031,32 @@ function hasAuthzDirShellWrite(command, tokens) {
|
|
|
833
1031
|
return true;
|
|
834
1032
|
}
|
|
835
1033
|
}
|
|
836
|
-
// Write/destructive + symlink plant bins with an authz path argument
|
|
837
|
-
// quote-strip resistant). Always run — do not gate on contiguous `.deft/authz`
|
|
838
|
-
// in the raw command (#3213). SYMLINK_PLANT_BINS: `ln -s forged .deft/authz/grants/x`
|
|
839
|
-
// must not fail-open as unclassifiable (SLizard residual).
|
|
1034
|
+
// Write/destructive + symlink plant + archive/alt-write bins with an authz path argument
|
|
1035
|
+
// (pathish = quote-strip resistant). Always run — do not gate on contiguous `.deft/authz`
|
|
1036
|
+
// in the raw command (#3213 / #3245). SYMLINK_PLANT_BINS: `ln -s forged .deft/authz/grants/x`
|
|
1037
|
+
// must not fail-open as unclassifiable (SLizard residual). ARCHIVE_ALT_WRITE_BINS: tar/rclone
|
|
1038
|
+
// residual pathish without dest-flag perfection thrash.
|
|
840
1039
|
for (let ti = 0; ti < tokens.length; ti++) {
|
|
841
1040
|
const bare = binBareName(tokens[ti]);
|
|
842
1041
|
const n = normalizeToken(tokens[ti]);
|
|
843
1042
|
if (!INDIRECT_WRITE_BINS.has(n) &&
|
|
844
1043
|
!INDIRECT_WRITE_BINS.has(bare) &&
|
|
845
|
-
!SYMLINK_PLANT_BINS.has(bare)
|
|
1044
|
+
!SYMLINK_PLANT_BINS.has(bare) &&
|
|
1045
|
+
!ARCHIVE_ALT_WRITE_BINS.has(bare)) {
|
|
846
1046
|
continue;
|
|
847
1047
|
}
|
|
848
1048
|
for (let tj = ti + 1; tj < tokens.length; tj++) {
|
|
849
|
-
|
|
1049
|
+
const p = pathishToken(tokens[tj]);
|
|
1050
|
+
if (pathishIsAuthzDir(p))
|
|
850
1051
|
return true;
|
|
1052
|
+
for (const prefix of SOCAT_WRITE_ADDR_PREFIXES) {
|
|
1053
|
+
if (p.startsWith(prefix) && pathishIsAuthzDir(p.slice(prefix.length))) {
|
|
1054
|
+
return true;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
851
1057
|
}
|
|
852
1058
|
}
|
|
853
|
-
// Downloader/decoder destinations under .deft/authz (#3206 / #3213
|
|
1059
|
+
// Downloader/decoder destinations under .deft/authz (#3206 / #3213 / #3245 archive+alt).
|
|
854
1060
|
for (const dest of downloaderDecoderDestinations(tokens)) {
|
|
855
1061
|
if (pathishIsAuthzDir(dest))
|
|
856
1062
|
return true;
|
|
@@ -932,7 +1138,12 @@ function hasWriteShape(command, tokens) {
|
|
|
932
1138
|
if (command.includes(">"))
|
|
933
1139
|
return true;
|
|
934
1140
|
for (const t of tokens) {
|
|
935
|
-
|
|
1141
|
+
const bare = binBareName(t);
|
|
1142
|
+
if (INDIRECT_WRITE_BINS.has(normalizeToken(t)) || INDIRECT_WRITE_BINS.has(bare))
|
|
1143
|
+
return true;
|
|
1144
|
+
// #3245: archive extractors / alt downloaders are write-shaped (defense-in-depth for
|
|
1145
|
+
// unclassifiable plant paths under UAT — not bare curl $URL which stays non-write-shaped).
|
|
1146
|
+
if (ARCHIVE_ALT_WRITE_BINS.has(bare))
|
|
936
1147
|
return true;
|
|
937
1148
|
}
|
|
938
1149
|
return false;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-origin structural apply gate for scope:decompose (#3239 / epic #3237 Q2).
|
|
3
|
+
*
|
|
4
|
+
* Apply requires a human-origin grant bound to:
|
|
5
|
+
* project root identity (worktree and/or repo), parent path, draft/target path,
|
|
6
|
+
* SHA-256 of exact draft bytes, and op scope.decompose.apply.structural.
|
|
7
|
+
*
|
|
8
|
+
* --check stays ungated (caller responsibility). Missing / expired / mismatched /
|
|
9
|
+
* agent-origin grants fail closed before any child story writes.
|
|
10
|
+
*/
|
|
11
|
+
import { type AuthzDecisionCode, type HumanOriginGrant } from "./types.js";
|
|
12
|
+
export type DecomposeApplyDecisionCode = AuthzDecisionCode | "authz-grant-digest-mismatch" | "authz-grant-parent-mismatch" | "authz-grant-target-mismatch" | "authz-grant-project-mismatch" | "authz-grant-binding-incomplete";
|
|
13
|
+
export interface DecomposeApplyDecision {
|
|
14
|
+
readonly allowed: boolean;
|
|
15
|
+
readonly code: DecomposeApplyDecisionCode;
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
readonly humanApprovalRef: string | null;
|
|
18
|
+
readonly draftDigest: string;
|
|
19
|
+
}
|
|
20
|
+
export interface EvaluateDecomposeStructuralApplyInput {
|
|
21
|
+
readonly projectRoot: string;
|
|
22
|
+
/** Absolute or project-relative parent artifact path. */
|
|
23
|
+
readonly parentPath: string;
|
|
24
|
+
/** Absolute or project-relative draft path whose bytes were digested. */
|
|
25
|
+
readonly draftPath: string;
|
|
26
|
+
/** SHA-256 hex of the exact draft file bytes about to be applied. */
|
|
27
|
+
readonly draftDigest: string;
|
|
28
|
+
readonly grants?: readonly HumanOriginGrant[];
|
|
29
|
+
/** Optional GitHub-style repo identity (owner/name); compared when grant pins repo. */
|
|
30
|
+
readonly repo?: string | null;
|
|
31
|
+
readonly now?: Date;
|
|
32
|
+
}
|
|
33
|
+
/** SHA-256 hex of a buffer or utf8 string. */
|
|
34
|
+
export declare function sha256Hex(data: Buffer | string): string;
|
|
35
|
+
/** SHA-256 hex of file bytes at path (exact on-disk digest). */
|
|
36
|
+
export declare function sha256FileHex(path: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Exact operator CLI mint command for a structural decompose apply grant (#3291).
|
|
39
|
+
* Paths are project-relative POSIX when inside projectRoot; otherwise as provided.
|
|
40
|
+
* Parent / draft / repo tokens are shell-quoted when they contain whitespace or
|
|
41
|
+
* shell metacharacters. When POSIX and PowerShell apostrophe escapes differ,
|
|
42
|
+
* both forms are emitted on separate labeled lines (`bash:` / `pwsh:`) so each
|
|
43
|
+
* is independently copy-pasteable. Paths containing newline or NUL are refused
|
|
44
|
+
* (no rewritten fake path) so the operator renames before minting. Does not
|
|
45
|
+
* include agent/CI/TTY gates — those remain on the CLI multi-factor path.
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatDecomposeStructuralMintCommand(parentPath: string, draftPath: string, options?: {
|
|
48
|
+
readonly projectRoot?: string;
|
|
49
|
+
readonly repo?: string | null;
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* Normalize a path to project-relative POSIX form for grant binding compare.
|
|
53
|
+
* Returns null when the path escapes the project root.
|
|
54
|
+
*/
|
|
55
|
+
export declare function toProjectRelativePosix(projectRoot: string, path: string): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Evaluate whether a human-origin grant covers structural decompose apply for
|
|
58
|
+
* the exact draft digest + parent + target + project identity.
|
|
59
|
+
*/
|
|
60
|
+
export declare function evaluateDecomposeStructuralApply(input: EvaluateDecomposeStructuralApplyInput): DecomposeApplyDecision;
|
|
61
|
+
export interface MintDecomposeStructuralApplyGrantInput {
|
|
62
|
+
readonly projectRoot: string;
|
|
63
|
+
readonly parentPath: string;
|
|
64
|
+
readonly draftPath: string;
|
|
65
|
+
/** When omitted, digest is computed from draftPath on-disk bytes. */
|
|
66
|
+
readonly contentDigest?: string;
|
|
67
|
+
readonly actor?: string;
|
|
68
|
+
readonly repo?: string | null;
|
|
69
|
+
readonly expiresAt?: string | null;
|
|
70
|
+
readonly singleUse?: boolean;
|
|
71
|
+
readonly grantId?: string;
|
|
72
|
+
readonly now?: Date;
|
|
73
|
+
readonly pinActive?: boolean;
|
|
74
|
+
readonly eventRef?: string | null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Operator mint helper: human-origin grant bound to exact draft bytes + paths.
|
|
78
|
+
* Sole mint path remains mintHumanOriginGrant (operator-cli origin).
|
|
79
|
+
*/
|
|
80
|
+
export declare function mintDecomposeStructuralApplyGrant(input: MintDecomposeStructuralApplyGrantInput): HumanOriginGrant;
|
|
81
|
+
//# sourceMappingURL=decompose-apply.d.ts.map
|