@wrongstack/plugins 0.307.0 → 0.308.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/accessibility-auditor/index.d.ts +5 -0
- package/dist/accessibility-auditor.js +67 -11
- package/dist/code-metrics/index.d.ts +9 -0
- package/dist/code-metrics.js +42 -11
- package/dist/dep-guard.js +4 -2
- package/dist/dependency-vulnerability-gate/index.d.ts +4 -0
- package/dist/dependency-vulnerability-gate.js +435 -62
- package/dist/file-watcher.js +25 -10
- package/dist/format-on-save.js +10 -4
- package/dist/import-organizer/index.d.ts +4 -0
- package/dist/import-organizer.js +22 -1
- package/dist/index.js +509 -206
- package/dist/license-audit-gate/index.d.ts +1 -0
- package/dist/license-audit-gate.js +445 -83
- package/dist/lint-gate/index.d.ts +13 -0
- package/dist/lint-gate.js +13 -10
- package/dist/path-guard/glob.d.ts +23 -0
- package/dist/path-guard/index.d.ts +2 -0
- package/dist/path-guard.js +214 -32
- package/dist/prompt-firewall/index.d.ts +7 -0
- package/dist/prompt-firewall.js +92 -2
- package/dist/runtime/h1-state.d.ts +62 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/redos-guard.d.ts +69 -0
- package/dist/runtime/sandbox.d.ts +59 -0
- package/dist/runtime.js +210 -19
- package/dist/secret-scanner.js +16 -2
- package/dist/type-gate.js +33 -3
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -345,6 +345,83 @@ function releaseHandle(off) {
|
|
|
345
345
|
return null;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
// src/runtime/redos-guard.ts
|
|
349
|
+
import { Worker } from "node:worker_threads";
|
|
350
|
+
function withReDoSGuard(re, input, budgetMs = 50, options = {}) {
|
|
351
|
+
const opts = { budgetMs, ...options };
|
|
352
|
+
const start = Date.now();
|
|
353
|
+
const workerSource = buildWorkerSource(re.source, input, re.flags);
|
|
354
|
+
const worker = new Worker(workerSource, {
|
|
355
|
+
eval: true,
|
|
356
|
+
name: `redos-guard:${re.source.slice(0, 32)}`
|
|
357
|
+
});
|
|
358
|
+
return new Promise((resolve29) => {
|
|
359
|
+
let settled = false;
|
|
360
|
+
const onMessage = (msg) => {
|
|
361
|
+
if (settled) return;
|
|
362
|
+
settled = true;
|
|
363
|
+
clearTimeout(timer);
|
|
364
|
+
worker.terminate().catch(() => {
|
|
365
|
+
});
|
|
366
|
+
if (!msg.ok) {
|
|
367
|
+
resolve29({ timedOut: true, match: null });
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
resolve29({ timedOut: false, match: msg.match });
|
|
371
|
+
};
|
|
372
|
+
const onError = () => {
|
|
373
|
+
if (settled) return;
|
|
374
|
+
settled = true;
|
|
375
|
+
clearTimeout(timer);
|
|
376
|
+
worker.terminate().catch(() => {
|
|
377
|
+
});
|
|
378
|
+
resolve29({ timedOut: true, match: null });
|
|
379
|
+
};
|
|
380
|
+
const timer = setTimeout(() => {
|
|
381
|
+
if (settled) return;
|
|
382
|
+
settled = true;
|
|
383
|
+
const elapsedMs = Date.now() - start;
|
|
384
|
+
worker.terminate().catch(() => {
|
|
385
|
+
});
|
|
386
|
+
try {
|
|
387
|
+
opts.onTimeout?.({
|
|
388
|
+
regex: re,
|
|
389
|
+
input,
|
|
390
|
+
budgetMs: opts.budgetMs,
|
|
391
|
+
elapsedMs
|
|
392
|
+
});
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
resolve29({ timedOut: true, match: null });
|
|
396
|
+
}, opts.budgetMs);
|
|
397
|
+
timer.unref?.();
|
|
398
|
+
worker.on("message", onMessage);
|
|
399
|
+
worker.on("error", onError);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function buildWorkerSource(source, input, flags) {
|
|
403
|
+
const S = JSON.stringify(source);
|
|
404
|
+
const I = JSON.stringify(input);
|
|
405
|
+
const F = JSON.stringify(flags);
|
|
406
|
+
return `
|
|
407
|
+
const { parentPort } = require('node:worker_threads');
|
|
408
|
+
const source = ${S};
|
|
409
|
+
const input = ${I};
|
|
410
|
+
const flags = ${F};
|
|
411
|
+
try {
|
|
412
|
+
const re = new RegExp(source, flags);
|
|
413
|
+
const match = re.exec(input);
|
|
414
|
+
// parentPort.postMessage, NOT bare postMessage: with eval:true
|
|
415
|
+
// workers this Node version does not expose the bare postMessage
|
|
416
|
+
// global \u2014 the worker throws ReferenceError at startup and the
|
|
417
|
+
// host misreads it as a timeout (positive-path regression).
|
|
418
|
+
parentPort.postMessage({ ok: true, match });
|
|
419
|
+
} catch (err) {
|
|
420
|
+
parentPort.postMessage({ ok: false, error: err && err.message ? err.message : String(err) });
|
|
421
|
+
}
|
|
422
|
+
`;
|
|
423
|
+
}
|
|
424
|
+
|
|
348
425
|
// src/runtime/index.ts
|
|
349
426
|
var META_CHARS = /["'`;&|<>\r\n]/;
|
|
350
427
|
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
@@ -446,6 +523,7 @@ function runRunnerCommand(argv, options) {
|
|
|
446
523
|
}
|
|
447
524
|
let timedOut = false;
|
|
448
525
|
let spawnErrored = false;
|
|
526
|
+
const start = Date.now();
|
|
449
527
|
const stdoutChunks = [];
|
|
450
528
|
const stderrChunks = [];
|
|
451
529
|
let stdoutBytes = 0;
|
|
@@ -475,6 +553,15 @@ function runRunnerCommand(argv, options) {
|
|
|
475
553
|
timeout: options.timeoutMs,
|
|
476
554
|
signal: options.signal,
|
|
477
555
|
maxBuffer: MAX_BUFFER_BYTES,
|
|
556
|
+
// execFile defaults `encoding` to 'utf8', which makes the
|
|
557
|
+
// stdout/stderr `data` events emit *strings*. The chunk arrays
|
|
558
|
+
// below are typed Buffer[] and every consumer runs them through
|
|
559
|
+
// Buffer.concat(...).toString('utf8'), which throws
|
|
560
|
+
// ERR_INVALID_ARG_TYPE on any non-empty string output. Pin the
|
|
561
|
+
// streams to buffers so the declared contract holds (regression:
|
|
562
|
+
// runRunnerCommand crashed on any child that actually wrote
|
|
563
|
+
// output; only the maxBuffer fixture exercised this path).
|
|
564
|
+
encoding: "buffer",
|
|
478
565
|
windowsHide: true,
|
|
479
566
|
shell: false,
|
|
480
567
|
...invocation.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
|
|
@@ -503,11 +590,31 @@ function runRunnerCommand(argv, options) {
|
|
|
503
590
|
}
|
|
504
591
|
if (err) {
|
|
505
592
|
const anyErr = err;
|
|
593
|
+
if ((anyErr.killed === true || anyErr.signal === "SIGTERM") && // maxBuffer overflow must NOT be misreported as a timeout
|
|
594
|
+
// — it's a real failure (the child wrote too much) and
|
|
595
|
+
// downstream callers (type-gate/index.ts:227) return
|
|
596
|
+
// null on timedOut=true, which would silently swallow
|
|
597
|
+
// maxBuffer overflow into a confusing empty-output
|
|
598
|
+
// result. Skip the timeout resolve when the err shape
|
|
599
|
+
// names maxBuffer explicitly.
|
|
600
|
+
!/maxBuffer length exceeded/i.test(anyErr.message ?? "") && // And only count it as a timeout if the wall clock has
|
|
601
|
+
// actually elapsed past the budget. External SIGTERMs and
|
|
602
|
+
// races against the exit handler don't satisfy this.
|
|
603
|
+
Date.now() - start >= options.timeoutMs) {
|
|
604
|
+
resolvePromise({
|
|
605
|
+
code: null,
|
|
606
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
607
|
+
stderr: Buffer.concat(stderrChunks).toString("utf-8"),
|
|
608
|
+
timedOut: true,
|
|
609
|
+
spawnError: false
|
|
610
|
+
});
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
506
613
|
const code = typeof anyErr.code === "number" ? anyErr.code : 1;
|
|
507
614
|
resolvePromise({
|
|
508
615
|
code,
|
|
509
|
-
stdout: Buffer.concat(stdoutChunks).toString("
|
|
510
|
-
stderr: Buffer.concat(stderrChunks).toString("
|
|
616
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
617
|
+
stderr: Buffer.concat(stderrChunks).toString("utf-8"),
|
|
511
618
|
timedOut: false,
|
|
512
619
|
spawnError: false
|
|
513
620
|
});
|
|
@@ -523,7 +630,7 @@ function runRunnerCommand(argv, options) {
|
|
|
523
630
|
}
|
|
524
631
|
);
|
|
525
632
|
child.on("exit", (_code, signal) => {
|
|
526
|
-
if (signal !== null) {
|
|
633
|
+
if (signal !== null && Date.now() - start >= options.timeoutMs) {
|
|
527
634
|
timedOut = true;
|
|
528
635
|
}
|
|
529
636
|
});
|
|
@@ -626,15 +733,40 @@ var INPUT_BUTTON = /<input\b[^>]*\btype\s*=\s*["'](submit|button|reset)["'][^>]*
|
|
|
626
733
|
var ATTR_ID = /\bid\s*=\s*["']([^"']+)["']/gi;
|
|
627
734
|
var ATTR_ALT = /\balt\s*=/i;
|
|
628
735
|
var ATTR_ARIA_LABEL = /\b(?:aria-label|aria-labelledby)\s*=/i;
|
|
736
|
+
var ATTR_ARIA_DESCRIBEDBY = /\baria-describedby\s*=/i;
|
|
629
737
|
var ATTR_TITLE = /\btitle\s*=/i;
|
|
630
738
|
var ATTR_PLACEHOLDER = /\bplaceholder\s*=/i;
|
|
631
739
|
var ATTR_VALUE = /\bvalue\s*=/i;
|
|
740
|
+
var ATTR_ROLE_DECORATIVE = /\brole\s*=\s*["'](?:presentation|none)["']/i;
|
|
741
|
+
var SINGLE_FILE_LABEL_NOTE = "Single-file heuristic: a label declared in a sibling component file is not visible to this scan.";
|
|
742
|
+
function escapeRegExp(value) {
|
|
743
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
744
|
+
}
|
|
632
745
|
function hasMeaningfulAlt(tag) {
|
|
633
746
|
const m = ATTR_ALT.exec(tag);
|
|
634
747
|
ATTR_ALT.lastIndex = 0;
|
|
635
748
|
if (!m) return false;
|
|
636
749
|
const valMatch = tag.match(/\balt\s*=\s*["']?([^"'\s>]*)["']?/i);
|
|
637
|
-
|
|
750
|
+
const alt = valMatch ? valMatch[1].trim() : "";
|
|
751
|
+
if (alt.length > 0) return true;
|
|
752
|
+
return ATTR_ROLE_DECORATIVE.test(tag);
|
|
753
|
+
}
|
|
754
|
+
function hasFieldsetLegendLabel(tag, content) {
|
|
755
|
+
const labelledBy = tag.match(/\baria-labelledby\s*=\s*["']([^"']+)["']/i);
|
|
756
|
+
if (labelledBy?.[1]) {
|
|
757
|
+
for (const id of labelledBy[1].split(/\s+/).filter(Boolean)) {
|
|
758
|
+
const idRe = new RegExp(`\\bid\\s*=\\s*["']${escapeRegExp(id)}["']`, "i");
|
|
759
|
+
if (idRe.test(content)) return true;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const typeMatch = tag.match(/\btype\s*=\s*["']?([^"'\s>]*)["']?/i);
|
|
763
|
+
const type = typeMatch ? typeMatch[1].toLowerCase() : "text";
|
|
764
|
+
if (type === "checkbox" || type === "radio") {
|
|
765
|
+
return /<fieldset\b[\s\S]*?<legend\b[\s\S]*?<\/legend>[\s\S]*?<input\b[\s\S]*?<\/fieldset>/i.test(
|
|
766
|
+
content
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
return false;
|
|
638
770
|
}
|
|
639
771
|
async function auditFile(filePath, projectRoot) {
|
|
640
772
|
let content;
|
|
@@ -646,13 +778,14 @@ async function auditFile(filePath, projectRoot) {
|
|
|
646
778
|
const findings = [];
|
|
647
779
|
const lines = content.split(/\r?\n/);
|
|
648
780
|
const idsByValue = /* @__PURE__ */ new Map();
|
|
649
|
-
function add(line, rule, severity, message) {
|
|
781
|
+
function add(line, rule, severity, message, note) {
|
|
650
782
|
findings.push({
|
|
651
783
|
file: relative3(projectRoot, filePath),
|
|
652
784
|
line,
|
|
653
785
|
rule,
|
|
654
786
|
severity,
|
|
655
|
-
message
|
|
787
|
+
message,
|
|
788
|
+
...note ? { note } : {}
|
|
656
789
|
});
|
|
657
790
|
}
|
|
658
791
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -680,17 +813,36 @@ async function auditFile(filePath, projectRoot) {
|
|
|
680
813
|
continue;
|
|
681
814
|
}
|
|
682
815
|
const hasAriaLabel = ATTR_ARIA_LABEL.test(tag);
|
|
816
|
+
const hasDescribedBy = ATTR_ARIA_DESCRIBEDBY.test(tag);
|
|
683
817
|
const hasTitle = ATTR_TITLE.test(tag);
|
|
684
818
|
const idMatchLocal = tag.match(/\bid\s*=\s*["']([^"']+)["']/i);
|
|
685
819
|
const id = idMatchLocal ? idMatchLocal[1] : null;
|
|
686
820
|
let hasLabelFor = false;
|
|
687
821
|
if (id) {
|
|
688
|
-
const labelForRe = new RegExp(
|
|
822
|
+
const labelForRe = new RegExp(
|
|
823
|
+
`<label\\b[^>]*\\bfor\\s*=\\s*["']${escapeRegExp(id)}["']`,
|
|
824
|
+
"i"
|
|
825
|
+
);
|
|
689
826
|
hasLabelFor = labelForRe.test(content);
|
|
690
827
|
}
|
|
691
828
|
const wrappedInLabel = /<label\b[\s\S]*?<input\b[\s\S]*?<\/label>/i.test(content);
|
|
692
|
-
|
|
693
|
-
|
|
829
|
+
const hasLegend = hasFieldsetLegendLabel(tag, content);
|
|
830
|
+
const hasPrimaryLabel = hasAriaLabel || hasTitle || hasLabelFor || wrappedInLabel || hasLegend;
|
|
831
|
+
if (!hasPrimaryLabel && hasDescribedBy) {
|
|
832
|
+
add(
|
|
833
|
+
lineNo,
|
|
834
|
+
"low-contrast-placeholder",
|
|
835
|
+
"warning",
|
|
836
|
+
`<input type="${type}"> uses aria-describedby as a description (supplementary, not a primary label)`
|
|
837
|
+
);
|
|
838
|
+
} else if (!hasPrimaryLabel) {
|
|
839
|
+
add(
|
|
840
|
+
lineNo,
|
|
841
|
+
"missing-input-label",
|
|
842
|
+
"error",
|
|
843
|
+
`<input type="${type}"> is missing an associated label`,
|
|
844
|
+
SINGLE_FILE_LABEL_NOTE
|
|
845
|
+
);
|
|
694
846
|
}
|
|
695
847
|
if (ATTR_PLACEHOLDER.test(tag)) {
|
|
696
848
|
add(lineNo, "low-contrast-placeholder", "warning", "<input> uses placeholder text (often low contrast and disappears on input)");
|
|
@@ -750,15 +902,24 @@ async function auditPath(rawPath, cfg) {
|
|
|
750
902
|
truncated
|
|
751
903
|
};
|
|
752
904
|
}
|
|
905
|
+
function truncationWarning(result) {
|
|
906
|
+
if (!result.truncated) return "";
|
|
907
|
+
const unexamined = Math.max(0, result.fileCount - (result.scannedFiles ?? result.fileCount));
|
|
908
|
+
return `partial scan \u2014 ${unexamined} files not examined`;
|
|
909
|
+
}
|
|
753
910
|
function formatSummary(result) {
|
|
911
|
+
const trunc = truncationWarning(result);
|
|
754
912
|
if (result.findings.length === 0) {
|
|
755
|
-
|
|
913
|
+
const clean = `
|
|
756
914
|
\u2705 accessibility-auditor: no issues found in ${result.path} (${result.fileCount} file${result.fileCount === 1 ? "" : "s"}).`;
|
|
915
|
+
return trunc ? `${clean}
|
|
916
|
+
\u26A0\uFE0F ${trunc}` : clean;
|
|
757
917
|
}
|
|
758
918
|
const lines = result.findings.map((f) => ` - ${f.file}:${f.line} \u2014 ${f.message} (${f.rule})`);
|
|
759
919
|
return `
|
|
760
920
|
\u26A0\uFE0F accessibility-auditor: ${result.findings.length} issue(s) in ${result.path} (${result.fileCount} file${result.fileCount === 1 ? "" : "s"}):
|
|
761
|
-
` + lines.join("\n") + "\nConsider adding missing labels/alt text or resolving duplicate ids."
|
|
921
|
+
` + lines.join("\n") + "\nConsider adding missing labels/alt text or resolving duplicate ids." + (trunc ? `
|
|
922
|
+
\u26A0\uFE0F ${trunc}` : "");
|
|
762
923
|
}
|
|
763
924
|
var plugin = {
|
|
764
925
|
name: "accessibility-auditor",
|
|
@@ -829,9 +990,9 @@ var plugin = {
|
|
|
829
990
|
findingCount: result.findings.length,
|
|
830
991
|
when: (/* @__PURE__ */ new Date()).toISOString()
|
|
831
992
|
};
|
|
832
|
-
if (result.findings.length === 0) return;
|
|
993
|
+
if (result.findings.length === 0 && !result.truncated) return;
|
|
833
994
|
const summary = formatSummary(result);
|
|
834
|
-
if (cfg.severity === "block") {
|
|
995
|
+
if (cfg.severity === "block" && result.findings.length > 0) {
|
|
835
996
|
return { decision: "block", reason: summary };
|
|
836
997
|
}
|
|
837
998
|
return { additionalContext: summary };
|
|
@@ -872,6 +1033,7 @@ var plugin = {
|
|
|
872
1033
|
findingCount: result.findings.length,
|
|
873
1034
|
when: (/* @__PURE__ */ new Date()).toISOString()
|
|
874
1035
|
};
|
|
1036
|
+
const warning = truncationWarning(result);
|
|
875
1037
|
return {
|
|
876
1038
|
ok: true,
|
|
877
1039
|
path: result.path,
|
|
@@ -881,7 +1043,8 @@ var plugin = {
|
|
|
881
1043
|
// that reports few findings must not read as a clean result.
|
|
882
1044
|
truncated: result.truncated,
|
|
883
1045
|
findingCount: result.findings.length,
|
|
884
|
-
findings: result.findings
|
|
1046
|
+
findings: result.findings,
|
|
1047
|
+
...warning ? { additionalContext: `\u26A0\uFE0F ${warning}`, warning } : {}
|
|
885
1048
|
};
|
|
886
1049
|
}
|
|
887
1050
|
});
|
|
@@ -3299,9 +3462,9 @@ var plugin9 = {
|
|
|
3299
3462
|
const ti = input.toolInput ?? {};
|
|
3300
3463
|
const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"];
|
|
3301
3464
|
if (typeof raw !== "string" || raw.length === 0) return;
|
|
3302
|
-
const
|
|
3303
|
-
if (!
|
|
3304
|
-
const captured = await captureFileForHook(
|
|
3465
|
+
const safePath2 = resolveProjectPath3(raw);
|
|
3466
|
+
if (!safePath2) return;
|
|
3467
|
+
const captured = await captureFileForHook(safePath2, cfg.maxFileBytes, runtime.signal);
|
|
3305
3468
|
if (captured === "too-large") {
|
|
3306
3469
|
state9.skippedLarge += 1;
|
|
3307
3470
|
return;
|
|
@@ -3319,7 +3482,7 @@ var plugin9 = {
|
|
|
3319
3482
|
state9.captures += 1;
|
|
3320
3483
|
api.metrics.counter("captures");
|
|
3321
3484
|
api.emitCustom?.("checkpoint:captured", {
|
|
3322
|
-
path:
|
|
3485
|
+
path: safePath2,
|
|
3323
3486
|
bytes: captured.bytes,
|
|
3324
3487
|
hadContent: captured.content !== null,
|
|
3325
3488
|
// 32-bit unsigned hash of the captured bytes. Collisions
|
|
@@ -3363,12 +3526,12 @@ var plugin9 = {
|
|
|
3363
3526
|
const rejectedOutsideProject = [];
|
|
3364
3527
|
let skipped = 0;
|
|
3365
3528
|
for (const p of paths) {
|
|
3366
|
-
const
|
|
3367
|
-
if (!
|
|
3529
|
+
const safePath2 = resolveProjectPath3(p);
|
|
3530
|
+
if (!safePath2) {
|
|
3368
3531
|
rejectedOutsideProject.push(p);
|
|
3369
3532
|
continue;
|
|
3370
3533
|
}
|
|
3371
|
-
const captured = await captureFile(
|
|
3534
|
+
const captured = await captureFile(safePath2, cfg.maxFileBytes);
|
|
3372
3535
|
if (captured === "too-large") {
|
|
3373
3536
|
skipped += 1;
|
|
3374
3537
|
state9.skippedLarge += 1;
|
|
@@ -3601,36 +3764,65 @@ function countFunctions(content) {
|
|
|
3601
3764
|
for (const _match of content.matchAll(arrowRe)) count++;
|
|
3602
3765
|
return count;
|
|
3603
3766
|
}
|
|
3767
|
+
var COMPLEXITY_FORMULA = "control(if|else if|for|while|switch|catch) + (&& || ?? ||= &&= ??=) + ternary ?; optional chaining ?. is not counted";
|
|
3604
3768
|
function countComplexity(content) {
|
|
3605
3769
|
const controlRe = /\b(if|else\s+if|for|while|switch|catch)\b/g;
|
|
3606
|
-
const operatorRe = /[?&|]/g;
|
|
3607
3770
|
let complexity = 0;
|
|
3608
3771
|
controlRe.lastIndex = 0;
|
|
3609
3772
|
for (const _match of content.matchAll(controlRe)) complexity++;
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
const
|
|
3613
|
-
|
|
3773
|
+
for (let i = 0; i < content.length; i++) {
|
|
3774
|
+
const c = content[i];
|
|
3775
|
+
const n1 = content[i + 1];
|
|
3776
|
+
const n2 = content[i + 2];
|
|
3777
|
+
if (c === "?" && n1 === "?" && n2 === "=") {
|
|
3614
3778
|
complexity++;
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3779
|
+
i += 2;
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
if (c === "|" && n1 === "|" && n2 === "=" || c === "&" && n1 === "&" && n2 === "=") {
|
|
3783
|
+
complexity++;
|
|
3784
|
+
i += 2;
|
|
3785
|
+
continue;
|
|
3618
3786
|
}
|
|
3787
|
+
if (c === "?" && n1 === "?" || c === "|" && n1 === "|" || c === "&" && n1 === "&") {
|
|
3788
|
+
complexity++;
|
|
3789
|
+
i += 1;
|
|
3790
|
+
continue;
|
|
3791
|
+
}
|
|
3792
|
+
if (c === "?" && n1 === ".") {
|
|
3793
|
+
i += 1;
|
|
3794
|
+
continue;
|
|
3795
|
+
}
|
|
3796
|
+
if (c === "?") complexity++;
|
|
3619
3797
|
}
|
|
3620
3798
|
return complexity;
|
|
3621
3799
|
}
|
|
3622
3800
|
function analyzeFile(filePath, content) {
|
|
3801
|
+
if (content.length === 0) {
|
|
3802
|
+
return {
|
|
3803
|
+
file: relativePath(filePath),
|
|
3804
|
+
lines: 0,
|
|
3805
|
+
codeLines: 0,
|
|
3806
|
+
commentLines: 0,
|
|
3807
|
+
blankLines: 0,
|
|
3808
|
+
functionCount: 0,
|
|
3809
|
+
complexity: 0
|
|
3810
|
+
};
|
|
3811
|
+
}
|
|
3623
3812
|
const lines = content.split(/\r?\n/);
|
|
3624
3813
|
let codeLines = 0;
|
|
3625
3814
|
let commentLines = 0;
|
|
3626
3815
|
let blankLines = 0;
|
|
3627
3816
|
let inBlockComment = false;
|
|
3628
|
-
for (
|
|
3629
|
-
const line =
|
|
3817
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3818
|
+
const line = lines[i].trim();
|
|
3630
3819
|
if (line.length === 0) {
|
|
3631
3820
|
blankLines++;
|
|
3632
3821
|
continue;
|
|
3633
3822
|
}
|
|
3823
|
+
if (i === 0 && line.startsWith("#!")) {
|
|
3824
|
+
continue;
|
|
3825
|
+
}
|
|
3634
3826
|
if (inBlockComment) {
|
|
3635
3827
|
commentLines++;
|
|
3636
3828
|
if (line.includes("*/")) inBlockComment = false;
|
|
@@ -3791,7 +3983,8 @@ var plugin10 = {
|
|
|
3791
3983
|
files: state10.fileCount,
|
|
3792
3984
|
hookInvocations: state10.hookInvocationCount,
|
|
3793
3985
|
errors: state10.errorCount
|
|
3794
|
-
}
|
|
3986
|
+
},
|
|
3987
|
+
complexityFormula: COMPLEXITY_FORMULA
|
|
3795
3988
|
};
|
|
3796
3989
|
}
|
|
3797
3990
|
});
|
|
@@ -5860,10 +6053,12 @@ function parseInstallCommands(command) {
|
|
|
5860
6053
|
if (!cleaned) continue;
|
|
5861
6054
|
let name = cleaned;
|
|
5862
6055
|
let version = null;
|
|
5863
|
-
const pipMatch = /^([A-Za-z0-9_.-]+)\s*(
|
|
6056
|
+
const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(cleaned);
|
|
5864
6057
|
if (pipMatch?.[1]) {
|
|
5865
6058
|
name = pipMatch[1];
|
|
5866
|
-
|
|
6059
|
+
const op = pipMatch[2] ?? "";
|
|
6060
|
+
const ver = (pipMatch[3] ?? "").trim();
|
|
6061
|
+
version = op === "==" || op === "" ? ver || null : `${op}${ver}`;
|
|
5867
6062
|
} else {
|
|
5868
6063
|
const at = cleaned.lastIndexOf("@");
|
|
5869
6064
|
if (at > 0) {
|
|
@@ -6288,13 +6483,12 @@ function exceedsThreshold(report, threshold) {
|
|
|
6288
6483
|
if (thresholdRank == null || maxRank == null) return false;
|
|
6289
6484
|
return maxRank >= thresholdRank;
|
|
6290
6485
|
}
|
|
6291
|
-
var INSTALL_RE2 = /\b(?:npm\s+(?:install|i)|pnpm\s+add|yarn\s+add)\b/i;
|
|
6292
6486
|
function isInstallCommand(input) {
|
|
6293
6487
|
if (input.toolName === "install") return true;
|
|
6294
6488
|
if (input.toolName !== "bash" && input.toolName !== "exec") return false;
|
|
6295
6489
|
const ti = input.toolInput ?? {};
|
|
6296
6490
|
const command = typeof ti["command"] === "string" ? ti["command"] : "";
|
|
6297
|
-
return
|
|
6491
|
+
return parseInstallCommands(command).length > 0;
|
|
6298
6492
|
}
|
|
6299
6493
|
var LOCKFILE_MANAGERS = [
|
|
6300
6494
|
{ file: "pnpm-lock.yaml", manager: "pnpm" },
|
|
@@ -8067,18 +8261,8 @@ var feature_flag_tracker_default = plugin23;
|
|
|
8067
8261
|
|
|
8068
8262
|
// src/file-watcher/index.ts
|
|
8069
8263
|
import { watch as fsWatch } from "node:fs";
|
|
8070
|
-
import {
|
|
8264
|
+
import { join as join3 } from "node:path";
|
|
8071
8265
|
var API_VERSION16 = "^0.1.10";
|
|
8072
|
-
function withinProject3(p) {
|
|
8073
|
-
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
8074
|
-
const root = process.cwd();
|
|
8075
|
-
const resolved = isAbsolute12(p) ? resolve13(p) : resolve13(root, p);
|
|
8076
|
-
const rel = relative13(root, resolved);
|
|
8077
|
-
if (rel === "" || rel === ".") return true;
|
|
8078
|
-
if (rel.startsWith("..")) return false;
|
|
8079
|
-
if (isAbsolute12(rel)) return false;
|
|
8080
|
-
return true;
|
|
8081
|
-
}
|
|
8082
8266
|
var watch_idCounter = 0;
|
|
8083
8267
|
function nextId() {
|
|
8084
8268
|
return `watch_${++watch_idCounter}_${Date.now().toString(36)}`;
|
|
@@ -8159,7 +8343,7 @@ var plugin24 = {
|
|
|
8159
8343
|
}
|
|
8160
8344
|
const autoIndex = api.config.extensions?.["file-watcher"]?.["autoIndex"] ?? false;
|
|
8161
8345
|
const indexProjectRoot = api.config.extensions?.["file-watcher"]?.["indexProjectRoot"] ?? "";
|
|
8162
|
-
const safeIndexRoot = indexProjectRoot !== "" &&
|
|
8346
|
+
const safeIndexRoot = indexProjectRoot !== "" && withinProject(indexProjectRoot) ? indexProjectRoot : "";
|
|
8163
8347
|
if (indexProjectRoot !== "" && safeIndexRoot === "") {
|
|
8164
8348
|
api.log.warn(
|
|
8165
8349
|
"file-watcher: indexProjectRoot is outside the project root \u2014 using watched dirPath instead",
|
|
@@ -8304,7 +8488,7 @@ var plugin24 = {
|
|
|
8304
8488
|
}
|
|
8305
8489
|
const events = input["events"] ?? ["change", "add", "delete"];
|
|
8306
8490
|
const recursive = input["recursive"] ?? true;
|
|
8307
|
-
const bad = paths.find((p) => !
|
|
8491
|
+
const bad = paths.find((p) => !withinProject(p));
|
|
8308
8492
|
if (bad !== void 0) {
|
|
8309
8493
|
return {
|
|
8310
8494
|
ok: false,
|
|
@@ -8549,6 +8733,7 @@ async function formatFile(filePath, timeoutMs) {
|
|
|
8549
8733
|
}
|
|
8550
8734
|
const formatter = resolveFormatter();
|
|
8551
8735
|
if (!formatter) return null;
|
|
8736
|
+
const started = Date.now();
|
|
8552
8737
|
let bytesBefore;
|
|
8553
8738
|
try {
|
|
8554
8739
|
bytesBefore = (await stat4(filePath)).size;
|
|
@@ -8593,14 +8778,15 @@ async function formatFile(filePath, timeoutMs) {
|
|
|
8593
8778
|
} catch {
|
|
8594
8779
|
return null;
|
|
8595
8780
|
}
|
|
8781
|
+
const durationMs = Date.now() - started;
|
|
8596
8782
|
if (bytesAfter !== bytesBefore) {
|
|
8597
|
-
return { changed: true, bytesBefore, bytesAfter };
|
|
8783
|
+
return { changed: true, bytesBefore, bytesAfter, durationMs };
|
|
8598
8784
|
}
|
|
8599
8785
|
const hashAfter = await sha256File(filePath);
|
|
8600
8786
|
if (hashBefore === null || hashAfter === null) {
|
|
8601
|
-
return { changed: false, bytesBefore, bytesAfter };
|
|
8787
|
+
return { changed: false, bytesBefore, bytesAfter, durationMs };
|
|
8602
8788
|
}
|
|
8603
|
-
return { changed: hashBefore !== hashAfter, bytesBefore, bytesAfter };
|
|
8789
|
+
return { changed: hashBefore !== hashAfter, bytesBefore, bytesAfter, durationMs };
|
|
8604
8790
|
}
|
|
8605
8791
|
var plugin25 = {
|
|
8606
8792
|
name: "format-on-save",
|
|
@@ -8689,6 +8875,7 @@ var plugin25 = {
|
|
|
8689
8875
|
changed: result.changed,
|
|
8690
8876
|
bytesBefore: result.bytesBefore,
|
|
8691
8877
|
bytesAfter: result.bytesAfter,
|
|
8878
|
+
durationMs: result.durationMs,
|
|
8692
8879
|
when: (/* @__PURE__ */ new Date()).toISOString()
|
|
8693
8880
|
};
|
|
8694
8881
|
if (result.changed) {
|
|
@@ -8743,7 +8930,10 @@ var plugin25 = {
|
|
|
8743
8930
|
formatted: state23.formattedCount,
|
|
8744
8931
|
clean: state23.cleanCount,
|
|
8745
8932
|
errors: state23.errorCount,
|
|
8746
|
-
coveredSkips: state23.coveredSkipCount
|
|
8933
|
+
coveredSkips: state23.coveredSkipCount,
|
|
8934
|
+
bytesBefore: state23.lastResult?.bytesBefore ?? 0,
|
|
8935
|
+
bytesAfter: state23.lastResult?.bytesAfter ?? 0,
|
|
8936
|
+
durationMs: state23.lastResult?.durationMs ?? 0
|
|
8747
8937
|
},
|
|
8748
8938
|
lastResult: state23.lastResult
|
|
8749
8939
|
};
|
|
@@ -9316,7 +9506,7 @@ var git_autocommit_default = plugin26;
|
|
|
9316
9506
|
|
|
9317
9507
|
// src/gitignore-guard/index.ts
|
|
9318
9508
|
import { access as access3, readFile as readFile9, writeFile as writeFile2 } from "node:fs/promises";
|
|
9319
|
-
import { basename as basename4, isAbsolute as
|
|
9509
|
+
import { basename as basename4, isAbsolute as isAbsolute12, join as join4, relative as relative13, resolve as resolve13, sep as sep3 } from "node:path";
|
|
9320
9510
|
var API_VERSION19 = "^0.1.10";
|
|
9321
9511
|
var DEFAULT_ARTIFACT_PATTERNS = Object.freeze([
|
|
9322
9512
|
"dist/",
|
|
@@ -9438,9 +9628,9 @@ async function appendPatterns(target, patterns, limit) {
|
|
|
9438
9628
|
}
|
|
9439
9629
|
function projectRelativePath(rawPath, cwd) {
|
|
9440
9630
|
const root = cwd ?? process.cwd();
|
|
9441
|
-
const abs =
|
|
9442
|
-
const rel = toForwardSlashes(
|
|
9443
|
-
if (rel.startsWith("..") ||
|
|
9631
|
+
const abs = isAbsolute12(rawPath) ? rawPath : resolve13(root, rawPath);
|
|
9632
|
+
const rel = toForwardSlashes(relative13(root, abs));
|
|
9633
|
+
if (rel.startsWith("..") || isAbsolute12(rel) || rel === "") return null;
|
|
9444
9634
|
return { abs, rel, root };
|
|
9445
9635
|
}
|
|
9446
9636
|
var DEFAULTS21 = {
|
|
@@ -9752,7 +9942,7 @@ var gitignore_guard_default = plugin27;
|
|
|
9752
9942
|
// src/import-organizer/index.ts
|
|
9753
9943
|
import { spawn } from "node:child_process";
|
|
9754
9944
|
import { existsSync as existsSync3, statSync as statSync2 } from "node:fs";
|
|
9755
|
-
import { basename as basename5, isAbsolute as
|
|
9945
|
+
import { basename as basename5, isAbsolute as isAbsolute13 } from "node:path";
|
|
9756
9946
|
var ALLOWED_FIRST_TOKENS = /* @__PURE__ */ new Set([
|
|
9757
9947
|
"npx",
|
|
9758
9948
|
"pnpm",
|
|
@@ -9795,17 +9985,31 @@ function resolveAllowedCommand(command) {
|
|
|
9795
9985
|
const tokens = command.split(/\s+/).filter(Boolean);
|
|
9796
9986
|
if (tokens.length === 0) return null;
|
|
9797
9987
|
const head = tokens[0];
|
|
9988
|
+
if (PACKAGE_RUNNERS.has(head)) {
|
|
9989
|
+
let i = 1;
|
|
9990
|
+
while (i < tokens.length && (tokens[i] === "exec" || tokens[i] === "dlx" || tokens[i] === "run")) {
|
|
9991
|
+
i++;
|
|
9992
|
+
}
|
|
9993
|
+
const toolToken = tokens[i];
|
|
9994
|
+
if (!toolToken || !(toolToken in LOCAL_BIN_PACKAGES)) return null;
|
|
9995
|
+
}
|
|
9996
|
+
if (head === "node") {
|
|
9997
|
+
const target = tokens[1];
|
|
9998
|
+
if (!target) return null;
|
|
9999
|
+
const base = basename5(target);
|
|
10000
|
+
if (!(base in LOCAL_BIN_PACKAGES) && !ALLOWED_FIRST_TOKENS.has(base)) return null;
|
|
10001
|
+
}
|
|
9798
10002
|
const local = ALLOWED_FIRST_TOKENS.has(head) ? resolveLocalToolCommand(tokens) : null;
|
|
9799
10003
|
if (local) return local;
|
|
9800
10004
|
if (ALLOWED_FIRST_TOKENS.has(head)) {
|
|
9801
10005
|
return { cmd: head, args: tokens.slice(1) };
|
|
9802
10006
|
}
|
|
9803
|
-
if (
|
|
10007
|
+
if (isAbsolute13(head)) {
|
|
9804
10008
|
if (!withinProject(head)) return null;
|
|
9805
10009
|
const base = basename5(head);
|
|
9806
10010
|
if (ALLOWED_FIRST_TOKENS.has(base)) return { cmd: head, args: tokens.slice(1) };
|
|
9807
10011
|
}
|
|
9808
|
-
if (!
|
|
10012
|
+
if (!isAbsolute13(head) && withinProject(head)) {
|
|
9809
10013
|
const base = basename5(head);
|
|
9810
10014
|
if (ALLOWED_FIRST_TOKENS.has(base)) return { cmd: head, args: tokens.slice(1) };
|
|
9811
10015
|
}
|
|
@@ -10032,6 +10236,12 @@ var plugin28 = {
|
|
|
10032
10236
|
bytesAfter: result.bytesAfter,
|
|
10033
10237
|
when: (/* @__PURE__ */ new Date()).toISOString()
|
|
10034
10238
|
};
|
|
10239
|
+
if (/\boxlint\b/.test(result.command)) {
|
|
10240
|
+
return {
|
|
10241
|
+
additionalContext: `
|
|
10242
|
+
\u{1F4E6} import-organizer: oxlint has no import-organize support \u2014 ran '${result.command}' as a no-op for import sorting on '${filePath}'.`
|
|
10243
|
+
};
|
|
10244
|
+
}
|
|
10035
10245
|
if (result.changed) {
|
|
10036
10246
|
state25.organizedCount += 1;
|
|
10037
10247
|
const delta = result.bytesAfter - result.bytesBefore;
|
|
@@ -10342,7 +10552,7 @@ var injection_shield_default = plugin29;
|
|
|
10342
10552
|
|
|
10343
10553
|
// src/interface-contract-guard/index.ts
|
|
10344
10554
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
10345
|
-
import { isAbsolute as
|
|
10555
|
+
import { isAbsolute as isAbsolute14, relative as relative14, resolve as resolve14 } from "node:path";
|
|
10346
10556
|
var API_VERSION21 = "^0.1.10";
|
|
10347
10557
|
var state27 = {
|
|
10348
10558
|
scanCount: 0,
|
|
@@ -10375,7 +10585,7 @@ function toPosix5(p) {
|
|
|
10375
10585
|
return p.replace(/\\/g, "/");
|
|
10376
10586
|
}
|
|
10377
10587
|
function relativePath5(p) {
|
|
10378
|
-
return toPosix5(
|
|
10588
|
+
return toPosix5(relative14(process.cwd(), p));
|
|
10379
10589
|
}
|
|
10380
10590
|
var INTERFACE_RE = /(?:export\s+)?interface\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
|
|
10381
10591
|
function extractInterfaceNames(content) {
|
|
@@ -10403,7 +10613,7 @@ function collectImplementedNames(content, into) {
|
|
|
10403
10613
|
}
|
|
10404
10614
|
async function scanPath3(rawPath, cfg) {
|
|
10405
10615
|
const root = process.cwd();
|
|
10406
|
-
const resolved =
|
|
10616
|
+
const resolved = isAbsolute14(rawPath) ? resolve14(rawPath) : resolve14(root, rawPath);
|
|
10407
10617
|
const exts = normalizeExtensions4(cfg.extensions);
|
|
10408
10618
|
const allFiles = await collectSourceFilesAsync(resolved, { extensions: exts });
|
|
10409
10619
|
const files = allFiles.slice(0, cfg.maxFiles);
|
|
@@ -10493,7 +10703,7 @@ var plugin30 = {
|
|
|
10493
10703
|
const exts = normalizeExtensions4(cfg.extensions);
|
|
10494
10704
|
if (!matchesExtension(sourcePath, exts)) return;
|
|
10495
10705
|
state27.hookInvocationCount += 1;
|
|
10496
|
-
const resolved =
|
|
10706
|
+
const resolved = resolve14(process.cwd(), sourcePath);
|
|
10497
10707
|
let content;
|
|
10498
10708
|
try {
|
|
10499
10709
|
content = await readFile10(resolved, "utf-8");
|
|
@@ -10541,7 +10751,7 @@ If you changed member shapes, search the project for implementers/\`satisfies\`/
|
|
|
10541
10751
|
state27.findingCount += result.findings.length;
|
|
10542
10752
|
return {
|
|
10543
10753
|
ok: true,
|
|
10544
|
-
path: relativePath5(
|
|
10754
|
+
path: relativePath5(resolve14(process.cwd(), rawPath)),
|
|
10545
10755
|
scannedFiles: result.scannedFiles,
|
|
10546
10756
|
findings: result.findings,
|
|
10547
10757
|
// Say so when the corpus was cut short. A partial scan that
|
|
@@ -10622,7 +10832,7 @@ var interface_contract_guard_default = plugin30;
|
|
|
10622
10832
|
|
|
10623
10833
|
// src/knowledge-graph/index.ts
|
|
10624
10834
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
10625
|
-
import { dirname as dirname5, isAbsolute as
|
|
10835
|
+
import { dirname as dirname5, isAbsolute as isAbsolute15, relative as relative15, resolve as resolve15 } from "node:path";
|
|
10626
10836
|
import { atomicWrite as atomicWrite2, ensureDir as ensureDir2 } from "@wrongstack/core/utils";
|
|
10627
10837
|
var API_VERSION22 = "^0.1.10";
|
|
10628
10838
|
var state28 = {
|
|
@@ -10644,10 +10854,10 @@ var DEFAULTS25 = {
|
|
|
10644
10854
|
};
|
|
10645
10855
|
function resolveProjectPath5(rawPath, cwd = process.cwd()) {
|
|
10646
10856
|
if (typeof rawPath !== "string" || rawPath.length === 0) return null;
|
|
10647
|
-
const root =
|
|
10648
|
-
const resolved =
|
|
10649
|
-
const rel =
|
|
10650
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
10857
|
+
const root = resolve15(cwd);
|
|
10858
|
+
const resolved = isAbsolute15(rawPath) ? resolve15(rawPath) : resolve15(root, rawPath);
|
|
10859
|
+
const rel = relative15(root, resolved);
|
|
10860
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute15(rel)) return resolved;
|
|
10651
10861
|
return null;
|
|
10652
10862
|
}
|
|
10653
10863
|
function readConfig26(raw) {
|
|
@@ -10946,7 +11156,7 @@ var knowledge_graph_default = plugin31;
|
|
|
10946
11156
|
|
|
10947
11157
|
// src/license-audit-gate/index.ts
|
|
10948
11158
|
import { readFileSync as readFileSync8 } from "node:fs";
|
|
10949
|
-
import { resolve as
|
|
11159
|
+
import { resolve as resolve16 } from "node:path";
|
|
10950
11160
|
var API_VERSION23 = "^0.1.10";
|
|
10951
11161
|
var state29 = {
|
|
10952
11162
|
invocations: 0,
|
|
@@ -10996,24 +11206,10 @@ function extractLicenseStrings(pkg) {
|
|
|
10996
11206
|
}
|
|
10997
11207
|
return [...new Set(out)];
|
|
10998
11208
|
}
|
|
10999
|
-
var INSTALL_RE3 = /(?:^|[;&|]\s*)(npm|pnpm|yarn|bun)\s+(?:install|i|add)\s+([^;&|]+)/gi;
|
|
11000
11209
|
function parsePackageNames(command) {
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
const argString = m[2] ?? "";
|
|
11005
|
-
for (const token of argString.split(/\s+/)) {
|
|
11006
|
-
if (!token || token.startsWith("-")) continue;
|
|
11007
|
-
if (/^(\.|\/|file:|git\+|https?:)/i.test(token) || token.endsWith(".tgz")) continue;
|
|
11008
|
-
const cleaned = token.replace(/^['"]|['"]$/g, "");
|
|
11009
|
-
if (!cleaned) continue;
|
|
11010
|
-
let name = cleaned;
|
|
11011
|
-
const at = cleaned.lastIndexOf("@");
|
|
11012
|
-
if (at > 0) name = cleaned.slice(0, at);
|
|
11013
|
-
if (name) names.push(name);
|
|
11014
|
-
}
|
|
11015
|
-
}
|
|
11016
|
-
return [...new Set(names)];
|
|
11210
|
+
return [
|
|
11211
|
+
...new Set(parseInstallCommands(command).flatMap((entry) => entry.packages.map((pkg) => pkg.name)))
|
|
11212
|
+
];
|
|
11017
11213
|
}
|
|
11018
11214
|
function auditPackages(names, allowedLicenses) {
|
|
11019
11215
|
const results = [];
|
|
@@ -11022,7 +11218,7 @@ function auditPackages(names, allowedLicenses) {
|
|
|
11022
11218
|
for (const name of names) {
|
|
11023
11219
|
let licenses = [];
|
|
11024
11220
|
try {
|
|
11025
|
-
const pkgPath =
|
|
11221
|
+
const pkgPath = resolve16("node_modules", name, "package.json");
|
|
11026
11222
|
const raw = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
11027
11223
|
licenses = extractLicenseStrings(raw);
|
|
11028
11224
|
} catch {
|
|
@@ -11207,7 +11403,7 @@ import { readFileSync as readFileSync9 } from "node:fs";
|
|
|
11207
11403
|
import { mkdtemp, readFile as readFile11, rm, writeFile as writeFile3 } from "node:fs/promises";
|
|
11208
11404
|
import { createRequire as createRequire2 } from "node:module";
|
|
11209
11405
|
import { tmpdir } from "node:os";
|
|
11210
|
-
import { dirname as dirname6, isAbsolute as
|
|
11406
|
+
import { dirname as dirname6, isAbsolute as isAbsolute16, join as join5, relative as relative16, resolve as resolve17, sep as sep4 } from "node:path";
|
|
11211
11407
|
var API_VERSION24 = "^0.1.10";
|
|
11212
11408
|
var state30 = {
|
|
11213
11409
|
/** Total PreToolUse invocations. */
|
|
@@ -11247,19 +11443,19 @@ var LINTER_PACKAGES = {
|
|
|
11247
11443
|
};
|
|
11248
11444
|
var linterCache = new BoundedMap({ max: 32, ttlMs: 3e5 });
|
|
11249
11445
|
function isInside2(parent, candidate) {
|
|
11250
|
-
const rel =
|
|
11251
|
-
return rel === "" || !rel.startsWith(`..${sep4}`) && rel !== ".." && !
|
|
11446
|
+
const rel = relative16(parent, candidate);
|
|
11447
|
+
return rel === "" || !rel.startsWith(`..${sep4}`) && rel !== ".." && !isAbsolute16(rel);
|
|
11252
11448
|
}
|
|
11253
11449
|
function resolveLocalLinter(name, cwd) {
|
|
11254
11450
|
try {
|
|
11255
11451
|
const packageName = LINTER_PACKAGES[name];
|
|
11256
|
-
const requireFromProject = createRequire2(
|
|
11452
|
+
const requireFromProject = createRequire2(resolve17(cwd, "package.json"));
|
|
11257
11453
|
const packagePath = requireFromProject.resolve(`${packageName}/package.json`);
|
|
11258
11454
|
const packageJson = JSON.parse(readFileSync9(packagePath, "utf-8"));
|
|
11259
11455
|
const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.[name] ?? Object.values(packageJson.bin ?? {})[0];
|
|
11260
|
-
if (!relativeBin ||
|
|
11456
|
+
if (!relativeBin || isAbsolute16(relativeBin)) return null;
|
|
11261
11457
|
const packageDir = dirname6(packagePath);
|
|
11262
|
-
const entry =
|
|
11458
|
+
const entry = resolve17(packageDir, relativeBin);
|
|
11263
11459
|
if (!isInside2(packageDir, entry)) return null;
|
|
11264
11460
|
return {
|
|
11265
11461
|
cmd: process.execPath,
|
|
@@ -11311,9 +11507,10 @@ async function detectLinter(requested, cwd) {
|
|
|
11311
11507
|
}
|
|
11312
11508
|
async function lintContent(content, filePath, linter, timeoutMs, cwd, signal) {
|
|
11313
11509
|
const ext = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")) : ".ts";
|
|
11314
|
-
|
|
11315
|
-
const tmpFile = join5(tmpDir, `input${ext}`);
|
|
11510
|
+
let tmpDir;
|
|
11316
11511
|
try {
|
|
11512
|
+
tmpDir = await mkdtemp(join5(tmpdir(), "lint-gate-"));
|
|
11513
|
+
const tmpFile = join5(tmpDir, `input${ext}`);
|
|
11317
11514
|
await writeFile3(tmpFile, content, "utf-8");
|
|
11318
11515
|
const fullArgs = [...linter.args, tmpFile];
|
|
11319
11516
|
const result = await runCommand2(linter.cmd, fullArgs, timeoutMs, cwd, signal);
|
|
@@ -11324,14 +11521,15 @@ async function lintContent(content, filePath, linter, timeoutMs, cwd, signal) {
|
|
|
11324
11521
|
if (signal.aborted) throw signal.reason;
|
|
11325
11522
|
return null;
|
|
11326
11523
|
} finally {
|
|
11327
|
-
await rm(tmpDir, { recursive: true, force: true }).catch(() => void 0);
|
|
11524
|
+
if (tmpDir) await rm(tmpDir, { recursive: true, force: true }).catch(() => void 0);
|
|
11328
11525
|
}
|
|
11329
11526
|
}
|
|
11330
11527
|
async function lintAndFix(content, filePath, linter, timeoutMs, cwd, signal) {
|
|
11331
11528
|
const ext = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")) : ".ts";
|
|
11332
|
-
|
|
11333
|
-
const tmpFile = join5(tmpDir, `input${ext}`);
|
|
11529
|
+
let tmpDir;
|
|
11334
11530
|
try {
|
|
11531
|
+
tmpDir = await mkdtemp(join5(tmpdir(), "lint-gate-fix-"));
|
|
11532
|
+
const tmpFile = join5(tmpDir, `input${ext}`);
|
|
11335
11533
|
await writeFile3(tmpFile, content, "utf-8");
|
|
11336
11534
|
const fixArgs = linter.name === "biome" ? [linter.args[0], "check", "--write", tmpFile] : [linter.args[0], "--fix", tmpFile];
|
|
11337
11535
|
await runCommand2(linter.cmd, fixArgs, timeoutMs, cwd, signal);
|
|
@@ -11341,7 +11539,7 @@ async function lintAndFix(content, filePath, linter, timeoutMs, cwd, signal) {
|
|
|
11341
11539
|
if (signal.aborted) throw signal.reason;
|
|
11342
11540
|
return content;
|
|
11343
11541
|
} finally {
|
|
11344
|
-
await rm(tmpDir, { recursive: true, force: true }).catch(() => void 0);
|
|
11542
|
+
if (tmpDir) await rm(tmpDir, { recursive: true, force: true }).catch(() => void 0);
|
|
11345
11543
|
}
|
|
11346
11544
|
}
|
|
11347
11545
|
function parseLinterOutput(stdout, linterName) {
|
|
@@ -11436,7 +11634,7 @@ var plugin33 = {
|
|
|
11436
11634
|
state30.lastResult = null;
|
|
11437
11635
|
linterCache.clear();
|
|
11438
11636
|
const cfg = readConfig28(api.config.extensions?.["lint-gate"]);
|
|
11439
|
-
const cwd =
|
|
11637
|
+
const cwd = resolve17(api.config.cwd ?? process.cwd());
|
|
11440
11638
|
const linterReady = detectLinter(cfg.linter, cwd).then((linter) => {
|
|
11441
11639
|
if (!linter) {
|
|
11442
11640
|
api.log.warn("lint-gate: no linter found (biome or eslint) \u2014 hook will be a no-op", {
|
|
@@ -11595,9 +11793,9 @@ ${summary}${truncated}`
|
|
|
11595
11793
|
name: "lint-gate",
|
|
11596
11794
|
stage: "mutate",
|
|
11597
11795
|
timeoutMs: Math.max(1e3, cfg.timeoutMs + 1e3),
|
|
11598
|
-
//
|
|
11599
|
-
//
|
|
11600
|
-
failurePolicy: "
|
|
11796
|
+
// Fail closed: a linter crash or timeout must not let unlinted
|
|
11797
|
+
// content through in block mode (issue #363).
|
|
11798
|
+
failurePolicy: "closed"
|
|
11601
11799
|
});
|
|
11602
11800
|
api.tools.register({
|
|
11603
11801
|
name: "lint_gate_status",
|
|
@@ -11933,7 +12131,7 @@ var llm_cache_default = plugin34;
|
|
|
11933
12131
|
|
|
11934
12132
|
// src/loop-breaker/index.ts
|
|
11935
12133
|
import { execFile as execFile9 } from "node:child_process";
|
|
11936
|
-
import { isAbsolute as
|
|
12134
|
+
import { isAbsolute as isAbsolute17, relative as relative17 } from "node:path";
|
|
11937
12135
|
var state32 = {
|
|
11938
12136
|
lastFingerprint: null,
|
|
11939
12137
|
streak: 0,
|
|
@@ -12044,7 +12242,7 @@ function hashString2(value) {
|
|
|
12044
12242
|
return String(h >>> 0);
|
|
12045
12243
|
}
|
|
12046
12244
|
async function gitDiffFingerprint(cwd, targetPath, signal) {
|
|
12047
|
-
const pathspec =
|
|
12245
|
+
const pathspec = isAbsolute17(targetPath) ? relative17(cwd, targetPath) : targetPath;
|
|
12048
12246
|
if (!pathspec || pathspec === ".." || pathspec.startsWith("../") || pathspec.startsWith("..\\")) {
|
|
12049
12247
|
return null;
|
|
12050
12248
|
}
|
|
@@ -13579,6 +13777,29 @@ var plugin38 = {
|
|
|
13579
13777
|
var notify_hub_default = plugin38;
|
|
13580
13778
|
|
|
13581
13779
|
// src/path-guard/glob.ts
|
|
13780
|
+
var GLOB_REDOS_BUDGET_MS = 250;
|
|
13781
|
+
function mergeGuardedRegex(patterns) {
|
|
13782
|
+
const flags = /* @__PURE__ */ new Set();
|
|
13783
|
+
for (const p of patterns) {
|
|
13784
|
+
for (const f of p.flags) {
|
|
13785
|
+
if (f !== "g" && f !== "y") flags.add(f);
|
|
13786
|
+
}
|
|
13787
|
+
}
|
|
13788
|
+
const uniqueFlags = [...flags].join("");
|
|
13789
|
+
return new RegExp(patterns.map((p) => p.source).join("|"), uniqueFlags);
|
|
13790
|
+
}
|
|
13791
|
+
async function matchesAnyGuarded(path, patterns, options = {}) {
|
|
13792
|
+
const normalized = normalizePath2(path);
|
|
13793
|
+
if (patterns.length === 0) return false;
|
|
13794
|
+
const result = await withReDoSGuard(
|
|
13795
|
+
mergeGuardedRegex(patterns),
|
|
13796
|
+
normalized,
|
|
13797
|
+
options.budgetMs ?? GLOB_REDOS_BUDGET_MS,
|
|
13798
|
+
options.onTimeout ? { onTimeout: options.onTimeout } : {}
|
|
13799
|
+
);
|
|
13800
|
+
if (result.timedOut) return true;
|
|
13801
|
+
return result.match !== null;
|
|
13802
|
+
}
|
|
13582
13803
|
function compilePathGlob(pattern) {
|
|
13583
13804
|
const normalized = pattern.replace(/\\/g, "/");
|
|
13584
13805
|
let source = "";
|
|
@@ -13713,8 +13934,7 @@ function scopesMayOverlap(left, right) {
|
|
|
13713
13934
|
}
|
|
13714
13935
|
return hasPartialSegmentWildcard(left) && rightPrefix.startsWith(leftPrefix) || hasPartialSegmentWildcard(right) && leftPrefix.startsWith(rightPrefix);
|
|
13715
13936
|
}
|
|
13716
|
-
function
|
|
13717
|
-
if (matchesAny(target.path, patterns)) return true;
|
|
13937
|
+
function targetIntersectsScope(target, patternTexts) {
|
|
13718
13938
|
if (target.kind === "file") return false;
|
|
13719
13939
|
const normalized = normalizePath2(target.path).replace(/\/$/, "");
|
|
13720
13940
|
if (!isUnresolvedPathScope(normalized)) {
|
|
@@ -13726,6 +13946,14 @@ function targetIntersectsPatterns(target, patternTexts, patterns) {
|
|
|
13726
13946
|
}
|
|
13727
13947
|
return patternTexts.some((pattern) => scopesMayOverlap(normalized, normalizePath2(pattern)));
|
|
13728
13948
|
}
|
|
13949
|
+
function targetIntersectsPatterns(target, patternTexts, patterns) {
|
|
13950
|
+
if (matchesAny(target.path, patterns)) return true;
|
|
13951
|
+
return targetIntersectsScope(target, patternTexts);
|
|
13952
|
+
}
|
|
13953
|
+
async function targetIntersectsPatternsGuarded(target, patternTexts, patterns, options = {}) {
|
|
13954
|
+
if (await matchesAnyGuarded(target.path, patterns, options)) return true;
|
|
13955
|
+
return targetIntersectsScope(target, patternTexts);
|
|
13956
|
+
}
|
|
13729
13957
|
function targetFullyAllowed(target, allowTexts, allowRes) {
|
|
13730
13958
|
if (target.kind === "file") return matchesAny(target.path, allowRes);
|
|
13731
13959
|
const normalized = normalizePath2(target.path).replace(/\/$/, "");
|
|
@@ -14042,7 +14270,7 @@ function commandRecursivelyDeletes(command) {
|
|
|
14042
14270
|
)) {
|
|
14043
14271
|
return true;
|
|
14044
14272
|
}
|
|
14045
|
-
const destructive = /(?:^|[;&|\r\n]\s*|\{\s*|\(\s*|\bxargs(?:\s+-[^\s]+)*\s+)(rm|rmdir|del)\s+((?:"[^"]*"|'[^']*'|\\.|\{[^}]*\}|\([^()]*\)|[^;&|\r\n}])+)/gi;
|
|
14273
|
+
const destructive = /(?:^|[;&|\r\n]\s*|\{\s*|\(\s*|\bxargs(?:\s+-[^\s]+)*\s+)(rm|rmdir|del|rd|Remove-Item)\s+((?:"[^"]*"|'[^']*'|\\.|\{[^}]*\}|\([^()]*\)|[^;&|\r\n}])+)/gi;
|
|
14046
14274
|
let match = destructive.exec(stripped);
|
|
14047
14275
|
while (match !== null) {
|
|
14048
14276
|
const tool = match[1]?.toLowerCase();
|
|
@@ -14055,6 +14283,8 @@ function commandRecursivelyDeletes(command) {
|
|
|
14055
14283
|
if (token === "--") break;
|
|
14056
14284
|
if (tool === "rm") {
|
|
14057
14285
|
if (token === "--recursive" || /^-[^-]*[rR]/.test(token)) recursive = true;
|
|
14286
|
+
} else if (tool === "remove-item") {
|
|
14287
|
+
if (/^-Recurse$/i.test(token) || /^-r$/i.test(token)) recursive = true;
|
|
14058
14288
|
} else if (/^\/[a-z]*s[a-z]*$/i.test(token)) {
|
|
14059
14289
|
recursive = true;
|
|
14060
14290
|
}
|
|
@@ -14303,7 +14533,7 @@ function destructiveTargetsAtDepth(command, depth) {
|
|
|
14303
14533
|
}
|
|
14304
14534
|
targets.push(...destructiveTargetsAtDepth(executableBody, depth + 1));
|
|
14305
14535
|
}
|
|
14306
|
-
const destructive = /(?:^|[;&|\r\n]\s*|\{\s*|(?<![$(])\(\s*|\bxargs(?:\s+-[^\s]+)*\s+)(?:sudo\s+)?(rm|rmdir|del|unlink|truncate|shred|mv)\s+((?:"[^"]*"|'[^']*'|\\.|\{[^}]*\}|\([^()]*\)|[^;&|\r\n}])+)/gi;
|
|
14536
|
+
const destructive = /(?:^|[;&|\r\n]\s*|\{\s*|(?<![$(])\(\s*|\bxargs(?:\s+-[^\s]+)*\s+)(?:sudo\s+)?(rm|rmdir|del|rd|Remove-Item|unlink|truncate|shred|mv)\s+((?:"[^"]*"|'[^']*'|\\.|\{[^}]*\}|\([^()]*\)|[^;&|\r\n}])+)/gi;
|
|
14307
14537
|
let m = destructive.exec(normalizedCommand);
|
|
14308
14538
|
while (m !== null) {
|
|
14309
14539
|
if (!tokenIsQuoted(m, m[1] ?? "")) targets.push(...shellArgs(m[2] ?? ""));
|
|
@@ -14791,8 +15021,27 @@ function operationLabel(toolName) {
|
|
|
14791
15021
|
}
|
|
14792
15022
|
|
|
14793
15023
|
// src/path-guard/index.ts
|
|
15024
|
+
import { realpathSync } from "node:fs";
|
|
15025
|
+
import { resolve as resolve18 } from "node:path";
|
|
15026
|
+
function isSymlinkEscape(path, cwd) {
|
|
15027
|
+
if (!withinProject(path)) return false;
|
|
15028
|
+
try {
|
|
15029
|
+
const abs = resolve18(cwd ?? process.cwd(), path);
|
|
15030
|
+
const real = realpathSync(abs);
|
|
15031
|
+
return !withinProject(real);
|
|
15032
|
+
} catch {
|
|
15033
|
+
return false;
|
|
15034
|
+
}
|
|
15035
|
+
}
|
|
14794
15036
|
function createState() {
|
|
14795
|
-
return {
|
|
15037
|
+
return {
|
|
15038
|
+
invocations: 0,
|
|
15039
|
+
blocks: 0,
|
|
15040
|
+
warns: 0,
|
|
15041
|
+
redosTimeouts: 0,
|
|
15042
|
+
lastBlock: null,
|
|
15043
|
+
hookUnregister: null
|
|
15044
|
+
};
|
|
14796
15045
|
}
|
|
14797
15046
|
var states = /* @__PURE__ */ new WeakMap();
|
|
14798
15047
|
var latestState = createState();
|
|
@@ -14887,7 +15136,11 @@ var plugin39 = {
|
|
|
14887
15136
|
additionalContext: `path-guard (warn mode): ${subject} and this ${operation} would modify it. Double-check this is intentional.`
|
|
14888
15137
|
};
|
|
14889
15138
|
};
|
|
14890
|
-
const
|
|
15139
|
+
const bumpRedos = () => {
|
|
15140
|
+
state60.redosTimeouts += 1;
|
|
15141
|
+
api.metrics.counter("redos_timeouts");
|
|
15142
|
+
};
|
|
15143
|
+
const hook = async (input) => {
|
|
14891
15144
|
if (!cfg.enabled) return;
|
|
14892
15145
|
state60.invocations += 1;
|
|
14893
15146
|
const toolName = input.toolName ?? "";
|
|
@@ -14908,21 +15161,27 @@ var plugin39 = {
|
|
|
14908
15161
|
const effectiveCwd = effectiveToolCwd(ti["cwd"], input.cwd);
|
|
14909
15162
|
const recursivelyDeletes = commandRecursivelyDeletes(commandForInspection);
|
|
14910
15163
|
const deletesImplicitScope = commandDeletesImplicitScope(commandForInspection);
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
15164
|
+
const shellHits = await Promise.all(
|
|
15165
|
+
shellTargets.map(async (path) => {
|
|
15166
|
+
const target = {
|
|
15167
|
+
path: relativeToInvocationCwd(resolveTargetPath(path, effectiveCwd), input.cwd),
|
|
15168
|
+
kind: isRootPathScope(path) && deletesImplicitScope || isUnresolvedPathScope(path) || recursivelyDeletes && (isDirectoryAmbiguousPath(path) || hasConfiguredProtectedDescendant(path, cfg.protect)) ? "deletion-scope" : "file"
|
|
15169
|
+
};
|
|
15170
|
+
if (targetFullyAllowed(target, cfg.allow, allowRes)) return null;
|
|
15171
|
+
const protectedShellTarget = await targetIntersectsPatternsGuarded(target, cfg.protect, protectRes, { onTimeout: bumpRedos }) || await matchesAnyGuarded(`${target.path.replace(/\/$/, "")}/.path-guard-probe`, protectRes, {
|
|
15172
|
+
onTimeout: bumpRedos
|
|
15173
|
+
});
|
|
15174
|
+
return protectedShellTarget ? target : null;
|
|
15175
|
+
})
|
|
15176
|
+
);
|
|
15177
|
+
const firstProtected = shellHits.find((t) => t !== null);
|
|
15178
|
+
if (firstProtected) {
|
|
15179
|
+
return verdict(
|
|
15180
|
+
firstProtected.path,
|
|
15181
|
+
toolName,
|
|
15182
|
+
"destructive shell command",
|
|
15183
|
+
firstProtected.kind !== "file"
|
|
15184
|
+
);
|
|
14926
15185
|
}
|
|
14927
15186
|
const writes = writesToDisk({ ...input, toolInput: ti });
|
|
14928
15187
|
const hasStructuredTarget = [...PATH_FIELDS, ...PATH_LIST_FIELDS].some((field) => ti[field] !== void 0) || typeof ti["patch"] === "string";
|
|
@@ -14933,11 +15192,21 @@ var plugin39 = {
|
|
|
14933
15192
|
}
|
|
14934
15193
|
if (!writesToDisk({ ...input, toolInput: ti }) || isReadOnlyInvocation(toolName, ti)) return;
|
|
14935
15194
|
const targets = pathsFromToolInput(ti, toolName, input.cwd);
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
15195
|
+
const writeHits = await Promise.all(
|
|
15196
|
+
targets.map(async (target) => {
|
|
15197
|
+
if (target.kind === "file" && isSymlinkEscape(target.path, input.cwd)) return target;
|
|
15198
|
+
if (targetFullyAllowed(target, cfg.allow, allowRes)) return null;
|
|
15199
|
+
return targetIntersectsPatterns(target, cfg.protect, protectRes) ? target : null;
|
|
15200
|
+
})
|
|
15201
|
+
);
|
|
15202
|
+
const firstProtectedWrite = writeHits.find((t) => t !== null);
|
|
15203
|
+
if (firstProtectedWrite) {
|
|
15204
|
+
return verdict(
|
|
15205
|
+
firstProtectedWrite.path,
|
|
15206
|
+
toolName,
|
|
15207
|
+
operationLabel(toolName),
|
|
15208
|
+
firstProtectedWrite.kind !== "file"
|
|
15209
|
+
);
|
|
14941
15210
|
}
|
|
14942
15211
|
return;
|
|
14943
15212
|
};
|
|
@@ -14964,7 +15233,8 @@ var plugin39 = {
|
|
|
14964
15233
|
counters: {
|
|
14965
15234
|
invocations: state60.invocations,
|
|
14966
15235
|
blocks: state60.blocks,
|
|
14967
|
-
warns: state60.warns
|
|
15236
|
+
warns: state60.warns,
|
|
15237
|
+
redosTimeouts: state60.redosTimeouts
|
|
14968
15238
|
},
|
|
14969
15239
|
lastBlock: state60.lastBlock
|
|
14970
15240
|
};
|
|
@@ -14987,10 +15257,16 @@ var plugin39 = {
|
|
|
14987
15257
|
}
|
|
14988
15258
|
state60.hookUnregister = null;
|
|
14989
15259
|
}
|
|
14990
|
-
const final = {
|
|
15260
|
+
const final = {
|
|
15261
|
+
invocations: state60.invocations,
|
|
15262
|
+
blocks: state60.blocks,
|
|
15263
|
+
warns: state60.warns,
|
|
15264
|
+
redosTimeouts: state60.redosTimeouts
|
|
15265
|
+
};
|
|
14991
15266
|
state60.invocations = 0;
|
|
14992
15267
|
state60.blocks = 0;
|
|
14993
15268
|
state60.warns = 0;
|
|
15269
|
+
state60.redosTimeouts = 0;
|
|
14994
15270
|
state60.lastBlock = null;
|
|
14995
15271
|
states.delete(api);
|
|
14996
15272
|
api.log.info("path-guard: teardown complete", { final });
|
|
@@ -15003,7 +15279,8 @@ var plugin39 = {
|
|
|
15003
15279
|
counters: {
|
|
15004
15280
|
invocations: state60.invocations,
|
|
15005
15281
|
blocks: state60.blocks,
|
|
15006
|
-
warns: state60.warns
|
|
15282
|
+
warns: state60.warns,
|
|
15283
|
+
redosTimeouts: state60.redosTimeouts
|
|
15007
15284
|
}
|
|
15008
15285
|
};
|
|
15009
15286
|
}
|
|
@@ -15012,7 +15289,7 @@ var path_guard_default = plugin39;
|
|
|
15012
15289
|
|
|
15013
15290
|
// src/performance-regression-gate/index.ts
|
|
15014
15291
|
import { existsSync as existsSync5, readFileSync as readFileSync11 } from "node:fs";
|
|
15015
|
-
import { isAbsolute as
|
|
15292
|
+
import { isAbsolute as isAbsolute18, relative as relative18, resolve as resolve19 } from "node:path";
|
|
15016
15293
|
var API_VERSION26 = "^0.1.10";
|
|
15017
15294
|
var state36 = {
|
|
15018
15295
|
invocationCount: 0,
|
|
@@ -15035,21 +15312,21 @@ function readConfig35(raw) {
|
|
|
15035
15312
|
thresholdPercent: threshold
|
|
15036
15313
|
};
|
|
15037
15314
|
}
|
|
15038
|
-
function
|
|
15315
|
+
function withinProject3(p, cwd = process.cwd()) {
|
|
15039
15316
|
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
15040
15317
|
const root = resolve19(cwd);
|
|
15041
|
-
const resolved =
|
|
15042
|
-
const rel =
|
|
15318
|
+
const resolved = isAbsolute18(p) ? resolve19(p) : resolve19(root, p);
|
|
15319
|
+
const rel = relative18(root, resolved);
|
|
15043
15320
|
if (rel === "" || rel === ".") return true;
|
|
15044
15321
|
if (rel.startsWith("..")) return false;
|
|
15045
|
-
if (
|
|
15322
|
+
if (isAbsolute18(rel)) return false;
|
|
15046
15323
|
return true;
|
|
15047
15324
|
}
|
|
15048
15325
|
function resolveProjectPath6(rawPath, cwd = process.cwd()) {
|
|
15049
15326
|
if (typeof rawPath !== "string" || rawPath.length === 0) return null;
|
|
15050
15327
|
const root = resolve19(cwd);
|
|
15051
|
-
const resolved =
|
|
15052
|
-
if (!
|
|
15328
|
+
const resolved = isAbsolute18(rawPath) ? resolve19(rawPath) : resolve19(root, rawPath);
|
|
15329
|
+
if (!withinProject3(resolved, cwd)) return null;
|
|
15053
15330
|
return resolved;
|
|
15054
15331
|
}
|
|
15055
15332
|
function isValidNumber(n) {
|
|
@@ -15463,7 +15740,7 @@ var plugin_stack_observer_default = PLUGIN;
|
|
|
15463
15740
|
// src/pr-drafter/index.ts
|
|
15464
15741
|
import { execFile as execFile10 } from "node:child_process";
|
|
15465
15742
|
import { mkdir as mkdir2, writeFile as writeFile4 } from "node:fs/promises";
|
|
15466
|
-
import { dirname as dirname7, isAbsolute as
|
|
15743
|
+
import { dirname as dirname7, isAbsolute as isAbsolute19, relative as relative19, resolve as resolve20 } from "node:path";
|
|
15467
15744
|
var API_VERSION27 = "^0.1.10";
|
|
15468
15745
|
var state38 = {
|
|
15469
15746
|
commits: [],
|
|
@@ -15503,9 +15780,9 @@ function readConfig37(raw) {
|
|
|
15503
15780
|
function resolveProjectPath7(rawPath, cwd = process.cwd()) {
|
|
15504
15781
|
if (typeof rawPath !== "string" || rawPath.length === 0) return null;
|
|
15505
15782
|
const root = resolve20(cwd);
|
|
15506
|
-
const resolved =
|
|
15507
|
-
const rel =
|
|
15508
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
15783
|
+
const resolved = isAbsolute19(rawPath) ? resolve20(rawPath) : resolve20(root, rawPath);
|
|
15784
|
+
const rel = relative19(root, resolved);
|
|
15785
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute19(rel)) return resolved;
|
|
15509
15786
|
return null;
|
|
15510
15787
|
}
|
|
15511
15788
|
function runGit4(args, timeout) {
|
|
@@ -16204,6 +16481,7 @@ var state40 = {
|
|
|
16204
16481
|
requestRedactions: 0,
|
|
16205
16482
|
responseRedactions: 0,
|
|
16206
16483
|
blocked: 0,
|
|
16484
|
+
timeoutCount: 0,
|
|
16207
16485
|
byKind: /* @__PURE__ */ new Map(),
|
|
16208
16486
|
lastDetection: null,
|
|
16209
16487
|
extensionUnregister: null
|
|
@@ -16248,6 +16526,7 @@ var plugin43 = {
|
|
|
16248
16526
|
state40.requestRedactions = 0;
|
|
16249
16527
|
state40.responseRedactions = 0;
|
|
16250
16528
|
state40.blocked = 0;
|
|
16529
|
+
state40.timeoutCount = 0;
|
|
16251
16530
|
state40.byKind.clear();
|
|
16252
16531
|
state40.lastDetection = null;
|
|
16253
16532
|
if (state40.extensionUnregister) {
|
|
@@ -16270,7 +16549,16 @@ var plugin43 = {
|
|
|
16270
16549
|
async wrapProviderRunner(_ctx, request, inner) {
|
|
16271
16550
|
const req = request ?? {};
|
|
16272
16551
|
state40.invocations += 1;
|
|
16273
|
-
const
|
|
16552
|
+
const requestText = collectText(req);
|
|
16553
|
+
for (const extra of EXTRA_PATTERNS) {
|
|
16554
|
+
const guarded = await withReDoSGuard(extra.re, requestText, 250, {
|
|
16555
|
+
onTimeout: () => {
|
|
16556
|
+
state40.timeoutCount += 1;
|
|
16557
|
+
}
|
|
16558
|
+
});
|
|
16559
|
+
if (guarded.timedOut) break;
|
|
16560
|
+
}
|
|
16561
|
+
const detections = detectSecrets(requestText, cfg.allow);
|
|
16274
16562
|
if (detections.length > 0) {
|
|
16275
16563
|
state40.requestsWithSecrets += 1;
|
|
16276
16564
|
for (const d of detections) {
|
|
@@ -16340,7 +16628,8 @@ var plugin43 = {
|
|
|
16340
16628
|
requestsWithSecrets: state40.requestsWithSecrets,
|
|
16341
16629
|
requestRedactions: state40.requestRedactions,
|
|
16342
16630
|
responseRedactions: state40.responseRedactions,
|
|
16343
|
-
blocked: state40.blocked
|
|
16631
|
+
blocked: state40.blocked,
|
|
16632
|
+
timeoutCount: state40.timeoutCount
|
|
16344
16633
|
},
|
|
16345
16634
|
byKind: Object.fromEntries(state40.byKind),
|
|
16346
16635
|
lastDetection: state40.lastDetection
|
|
@@ -16396,7 +16685,7 @@ var prompt_firewall_default = plugin43;
|
|
|
16396
16685
|
|
|
16397
16686
|
// src/refactor-suggester/index.ts
|
|
16398
16687
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
16399
|
-
import { isAbsolute as
|
|
16688
|
+
import { isAbsolute as isAbsolute20, relative as relative20, resolve as resolve21 } from "node:path";
|
|
16400
16689
|
var API_VERSION28 = "^0.1.10";
|
|
16401
16690
|
var HOOK_WARNING_COOLDOWN_MS2 = 6e4;
|
|
16402
16691
|
var state41 = {
|
|
@@ -16440,7 +16729,7 @@ function toPosix6(p) {
|
|
|
16440
16729
|
return p.replace(/\\/g, "/");
|
|
16441
16730
|
}
|
|
16442
16731
|
function relativePath6(p) {
|
|
16443
|
-
return toPosix6(
|
|
16732
|
+
return toPosix6(relative20(process.cwd(), p));
|
|
16444
16733
|
}
|
|
16445
16734
|
function leadingIndentLevel(line) {
|
|
16446
16735
|
const leading = line.match(/^(\s*)/)?.[1] ?? "";
|
|
@@ -16527,7 +16816,7 @@ function detectSmells(filePath, content, rules) {
|
|
|
16527
16816
|
}
|
|
16528
16817
|
async function scanPath4(rawPath, cfg) {
|
|
16529
16818
|
const root = process.cwd();
|
|
16530
|
-
const resolved =
|
|
16819
|
+
const resolved = isAbsolute20(rawPath) ? resolve21(rawPath) : resolve21(root, rawPath);
|
|
16531
16820
|
const exts = normalizeExtensions5(cfg.extensions);
|
|
16532
16821
|
const files = await collectSourceFilesAsync(resolved, { extensions: exts });
|
|
16533
16822
|
const suggestions = [];
|
|
@@ -17423,6 +17712,7 @@ function createState2() {
|
|
|
17423
17712
|
allowCount: 0,
|
|
17424
17713
|
/** PostToolUse: secrets detected in tool output. */
|
|
17425
17714
|
leakCount: 0,
|
|
17715
|
+
timeoutCount: 0,
|
|
17426
17716
|
/** Most recent PreToolUse block — surfaced by `secret_scanner_status`. */
|
|
17427
17717
|
lastBlock: null,
|
|
17428
17718
|
/** Most recent PostToolUse leak — surfaced by `secret_scanner_status`. */
|
|
@@ -17591,7 +17881,19 @@ function buildHook(cfg, log, runtime) {
|
|
|
17591
17881
|
const { state: state60 } = runtime;
|
|
17592
17882
|
if (!cfg.enabled) return;
|
|
17593
17883
|
const toolName = input.toolName ?? "unknown";
|
|
17594
|
-
|
|
17884
|
+
let matched;
|
|
17885
|
+
try {
|
|
17886
|
+
matched = scanInput(input.toolInput);
|
|
17887
|
+
} catch (err) {
|
|
17888
|
+
if (String(err).includes("ReDoS")) {
|
|
17889
|
+
state60.timeoutCount += 1;
|
|
17890
|
+
return {
|
|
17891
|
+
decision: "block",
|
|
17892
|
+
reason: "secret-scanner: ReDoS timeout \u2014 regex scan exceeded the wall-clock budget. Fail-closed: treated as a block."
|
|
17893
|
+
};
|
|
17894
|
+
}
|
|
17895
|
+
throw err;
|
|
17896
|
+
}
|
|
17595
17897
|
if (!matched) return;
|
|
17596
17898
|
const summary = matched.join(", ");
|
|
17597
17899
|
const when = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -17769,7 +18071,8 @@ var plugin47 = {
|
|
|
17769
18071
|
block: state60.blockCount,
|
|
17770
18072
|
redact: state60.redactCount,
|
|
17771
18073
|
allow: state60.allowCount,
|
|
17772
|
-
leak: state60.leakCount
|
|
18074
|
+
leak: state60.leakCount,
|
|
18075
|
+
timeoutCount: state60.timeoutCount
|
|
17773
18076
|
},
|
|
17774
18077
|
lastBlock: state60.lastBlock,
|
|
17775
18078
|
lastLeak: state60.lastLeak
|
|
@@ -17859,7 +18162,7 @@ var secret_scanner_default = plugin47;
|
|
|
17859
18162
|
|
|
17860
18163
|
// src/security-hotspot-scanner/index.ts
|
|
17861
18164
|
import { readdir as readdir2, readFile as readFile13, stat as stat5 } from "node:fs/promises";
|
|
17862
|
-
import { isAbsolute as
|
|
18165
|
+
import { isAbsolute as isAbsolute21, relative as relative21, resolve as resolve22 } from "node:path";
|
|
17863
18166
|
var API_VERSION31 = "^0.1.10";
|
|
17864
18167
|
var state44 = {
|
|
17865
18168
|
scanCount: 0,
|
|
@@ -17950,7 +18253,7 @@ function isSourceFile2(filePath, extensions) {
|
|
|
17950
18253
|
async function scanPath5(inputPath, cfg) {
|
|
17951
18254
|
const start = Date.now();
|
|
17952
18255
|
const root = process.cwd();
|
|
17953
|
-
const resolved =
|
|
18256
|
+
const resolved = isAbsolute21(inputPath) ? resolve22(inputPath) : resolve22(root, inputPath);
|
|
17954
18257
|
if (!withinProject(inputPath)) {
|
|
17955
18258
|
return {
|
|
17956
18259
|
path: inputPath,
|
|
@@ -17971,7 +18274,7 @@ async function scanPath5(inputPath, cfg) {
|
|
|
17971
18274
|
filesScanned += 1;
|
|
17972
18275
|
const findings = scanSource(content, maxPerFile);
|
|
17973
18276
|
for (const f of findings) {
|
|
17974
|
-
allFindings.push({ ...f, snippet: `${
|
|
18277
|
+
allFindings.push({ ...f, snippet: `${relative21(root, filePath)}:${f.line}: ${f.snippet}` });
|
|
17975
18278
|
if (allFindings.length >= cfg.maxFindings) return;
|
|
17976
18279
|
}
|
|
17977
18280
|
} catch {
|
|
@@ -18255,7 +18558,7 @@ var security_hotspot_scanner_default = plugin48;
|
|
|
18255
18558
|
|
|
18256
18559
|
// src/semantic-search-indexer/index.ts
|
|
18257
18560
|
import * as fs2 from "node:fs/promises";
|
|
18258
|
-
import { isAbsolute as
|
|
18561
|
+
import { isAbsolute as isAbsolute22, relative as relative22, resolve as resolve23 } from "node:path";
|
|
18259
18562
|
import { DEFAULT_WALK_IGNORE_DIRS } from "@wrongstack/core/utils";
|
|
18260
18563
|
var API_VERSION32 = "^0.1.10";
|
|
18261
18564
|
var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -18333,21 +18636,21 @@ function readConfig45(raw) {
|
|
|
18333
18636
|
function normalizeSlashes2(p) {
|
|
18334
18637
|
return p.replace(/\\/g, "/");
|
|
18335
18638
|
}
|
|
18336
|
-
function
|
|
18639
|
+
function withinProject4(p) {
|
|
18337
18640
|
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
18338
18641
|
const root = normalizeSlashes2(process.cwd());
|
|
18339
|
-
const resolved = normalizeSlashes2(
|
|
18340
|
-
const rel = normalizeSlashes2(
|
|
18642
|
+
const resolved = normalizeSlashes2(isAbsolute22(p) ? resolve23(p) : resolve23(root, p));
|
|
18643
|
+
const rel = normalizeSlashes2(relative22(root, resolved));
|
|
18341
18644
|
if (rel === "" || rel === ".") return true;
|
|
18342
18645
|
if (rel.startsWith("..")) return false;
|
|
18343
|
-
if (
|
|
18646
|
+
if (isAbsolute22(rel)) return false;
|
|
18344
18647
|
return true;
|
|
18345
18648
|
}
|
|
18346
18649
|
function resolveProjectPath8(p) {
|
|
18347
18650
|
const raw = typeof p === "string" && p.length > 0 ? p : ".";
|
|
18348
|
-
if (!
|
|
18651
|
+
if (!withinProject4(raw)) return null;
|
|
18349
18652
|
const root = normalizeSlashes2(process.cwd());
|
|
18350
|
-
return normalizeSlashes2(
|
|
18653
|
+
return normalizeSlashes2(isAbsolute22(raw) ? resolve23(raw) : resolve23(root, raw));
|
|
18351
18654
|
}
|
|
18352
18655
|
function tokenize(text, minLength) {
|
|
18353
18656
|
const tokens = [];
|
|
@@ -18432,7 +18735,7 @@ async function walkDirectory(absPath, cfg, excludes, fileBatch) {
|
|
|
18432
18735
|
return;
|
|
18433
18736
|
}
|
|
18434
18737
|
const absChild = normalizeSlashes2(resolve23(absPath, ent.name));
|
|
18435
|
-
const relChild = normalizeSlashes2(
|
|
18738
|
+
const relChild = normalizeSlashes2(relative22(root, absChild));
|
|
18436
18739
|
if (relChild === "" || relChild === ".") continue;
|
|
18437
18740
|
if (excludes.some((re) => re.test(relChild))) continue;
|
|
18438
18741
|
if (ent.isDirectory()) {
|
|
@@ -18474,7 +18777,7 @@ async function buildIndex(rootPath, cfg) {
|
|
|
18474
18777
|
return;
|
|
18475
18778
|
}
|
|
18476
18779
|
if (rootStats.isFile()) {
|
|
18477
|
-
const relPath = normalizeSlashes2(
|
|
18780
|
+
const relPath = normalizeSlashes2(relative22(normalizeSlashes2(process.cwd()), rootPath));
|
|
18478
18781
|
await indexFileFromStats(rootPath, relPath === "" ? "." : relPath, rootStats, cfg);
|
|
18479
18782
|
state45.fileCount = state45.index.files.size;
|
|
18480
18783
|
} else if (rootStats.isDirectory()) {
|
|
@@ -18771,14 +19074,14 @@ import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
|
18771
19074
|
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
18772
19075
|
import { execFile as execFile12 } from "node:child_process";
|
|
18773
19076
|
import { access as access4, readFile as readFile15, readdir as readdir4, writeFile as writeFile5 } from "node:fs/promises";
|
|
18774
|
-
import { isAbsolute as
|
|
19077
|
+
import { isAbsolute as isAbsolute23, join as join6, relative as relative23, resolve as resolve24 } from "node:path";
|
|
18775
19078
|
var API_VERSION33 = "^0.1.10";
|
|
18776
19079
|
function resolveProjectRoot(rawCwd, root = process.cwd()) {
|
|
18777
19080
|
if (typeof rawCwd !== "string" || rawCwd.length === 0) return root;
|
|
18778
19081
|
const base = resolve24(root);
|
|
18779
|
-
const resolved =
|
|
18780
|
-
const rel =
|
|
18781
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
19082
|
+
const resolved = isAbsolute23(rawCwd) ? resolve24(rawCwd) : resolve24(base, rawCwd);
|
|
19083
|
+
const rel = relative23(base, resolved);
|
|
19084
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute23(rel)) return resolved;
|
|
18782
19085
|
return null;
|
|
18783
19086
|
}
|
|
18784
19087
|
var state46 = {
|
|
@@ -19801,16 +20104,16 @@ var session_recap_default = plugin51;
|
|
|
19801
20104
|
// src/shell-check/index.ts
|
|
19802
20105
|
import { execFile as execFile13 } from "node:child_process";
|
|
19803
20106
|
import { readdir as readdir5 } from "node:fs/promises";
|
|
19804
|
-
import { isAbsolute as
|
|
20107
|
+
import { isAbsolute as isAbsolute24, join as join7, relative as relative24, resolve as resolve25 } from "node:path";
|
|
19805
20108
|
var API_VERSION34 = "^0.1.10";
|
|
19806
|
-
function
|
|
20109
|
+
function withinProject5(p) {
|
|
19807
20110
|
if (p.startsWith("-")) return false;
|
|
19808
20111
|
const root = process.cwd();
|
|
19809
|
-
const resolved =
|
|
19810
|
-
const rel =
|
|
20112
|
+
const resolved = isAbsolute24(p) ? resolve25(p) : resolve25(root, p);
|
|
20113
|
+
const rel = relative24(root, resolved);
|
|
19811
20114
|
if (rel === "" || rel === ".") return true;
|
|
19812
20115
|
if (rel.startsWith("..")) return false;
|
|
19813
|
-
if (
|
|
20116
|
+
if (isAbsolute24(rel)) return false;
|
|
19814
20117
|
return true;
|
|
19815
20118
|
}
|
|
19816
20119
|
var MAX_PATH_LEN = 4096;
|
|
@@ -19987,7 +20290,7 @@ var plugin52 = {
|
|
|
19987
20290
|
const pattern = inp.pattern ?? "";
|
|
19988
20291
|
const severity = inp.severity ?? "warning";
|
|
19989
20292
|
state48.invocationCount += 1;
|
|
19990
|
-
const pathIsSafe = (p) => typeof p === "string" && p.length > 0 && p.length <= MAX_PATH_LEN &&
|
|
20293
|
+
const pathIsSafe = (p) => typeof p === "string" && p.length > 0 && p.length <= MAX_PATH_LEN && withinProject5(p);
|
|
19991
20294
|
if (!pathIsSafe(directory)) {
|
|
19992
20295
|
return {
|
|
19993
20296
|
ok: false,
|
|
@@ -20103,7 +20406,7 @@ var shell_check_default = plugin52;
|
|
|
20103
20406
|
|
|
20104
20407
|
// src/smart-rename/index.ts
|
|
20105
20408
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "node:fs";
|
|
20106
|
-
import { extname as extname6, isAbsolute as
|
|
20409
|
+
import { extname as extname6, isAbsolute as isAbsolute25, relative as relative25, resolve as resolve26 } from "node:path";
|
|
20107
20410
|
var API_VERSION35 = "^0.1.10";
|
|
20108
20411
|
var state49 = {
|
|
20109
20412
|
renameCount: 0,
|
|
@@ -20122,21 +20425,21 @@ function readConfig47(raw) {
|
|
|
20122
20425
|
extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS44.extensions
|
|
20123
20426
|
};
|
|
20124
20427
|
}
|
|
20125
|
-
function
|
|
20428
|
+
function withinProject6(p) {
|
|
20126
20429
|
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
20127
20430
|
const root = process.cwd();
|
|
20128
|
-
const resolved =
|
|
20129
|
-
const rel =
|
|
20431
|
+
const resolved = isAbsolute25(p) ? resolve26(p) : resolve26(root, p);
|
|
20432
|
+
const rel = relative25(root, resolved);
|
|
20130
20433
|
if (rel === "" || rel === ".") return true;
|
|
20131
20434
|
if (rel.startsWith("..")) return false;
|
|
20132
|
-
if (
|
|
20435
|
+
if (isAbsolute25(rel)) return false;
|
|
20133
20436
|
return true;
|
|
20134
20437
|
}
|
|
20135
20438
|
function toPosix7(p) {
|
|
20136
20439
|
return p.replace(/\\/g, "/");
|
|
20137
20440
|
}
|
|
20138
20441
|
function relativePath7(p) {
|
|
20139
|
-
return toPosix7(
|
|
20442
|
+
return toPosix7(relative25(process.cwd(), p));
|
|
20140
20443
|
}
|
|
20141
20444
|
function escapeRegex2(s) {
|
|
20142
20445
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -20221,7 +20524,7 @@ var plugin53 = {
|
|
|
20221
20524
|
if (!isIdentifier(newName)) {
|
|
20222
20525
|
return { ok: false, error: `newName "${newName}" is not a valid identifier` };
|
|
20223
20526
|
}
|
|
20224
|
-
if (!
|
|
20527
|
+
if (!withinProject6(rawPath)) {
|
|
20225
20528
|
return { ok: false, error: "path is outside the project root" };
|
|
20226
20529
|
}
|
|
20227
20530
|
const ext = extname6(rawPath).toLowerCase();
|
|
@@ -20499,7 +20802,7 @@ function isWrappedAsLinkOrCode(line, name) {
|
|
|
20499
20802
|
if (lower.includes(`[\``) && lower.includes(`\`](`)) return true;
|
|
20500
20803
|
return false;
|
|
20501
20804
|
}
|
|
20502
|
-
function
|
|
20805
|
+
function escapeRegExp2(s) {
|
|
20503
20806
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20504
20807
|
}
|
|
20505
20808
|
function mapMarkdownFences(lines) {
|
|
@@ -20524,7 +20827,7 @@ function findUnlinkedReferences(lines, names) {
|
|
|
20524
20827
|
const line = lines[i];
|
|
20525
20828
|
if (line.length === 0) continue;
|
|
20526
20829
|
for (const name of names) {
|
|
20527
|
-
const re = new RegExp(`(^|[^\\w-])${
|
|
20830
|
+
const re = new RegExp(`(^|[^\\w-])${escapeRegExp2(name)}(?![\\w-])`, "i");
|
|
20528
20831
|
if (re.test(line) && !isWrappedAsLinkOrCode(line, name)) {
|
|
20529
20832
|
if (!found.has(name)) found.set(name, true);
|
|
20530
20833
|
}
|
|
@@ -20553,7 +20856,7 @@ function wrapLineReferences(line) {
|
|
|
20553
20856
|
let cursor = 0;
|
|
20554
20857
|
const spans = [];
|
|
20555
20858
|
for (const name of PLUGIN_NAMES) {
|
|
20556
|
-
const re = new RegExp(`(^|[^\\w-])(${
|
|
20859
|
+
const re = new RegExp(`(^|[^\\w-])(${escapeRegExp2(name)})(?![\\w-])`, "gi");
|
|
20557
20860
|
let m;
|
|
20558
20861
|
re.lastIndex = 0;
|
|
20559
20862
|
while ((m = re.exec(line)) !== null) {
|
|
@@ -20774,7 +21077,7 @@ var spec_linker_default = plugin54;
|
|
|
20774
21077
|
|
|
20775
21078
|
// src/template-engine/index.ts
|
|
20776
21079
|
import { readFile as readFile17, writeFile as writeFile6 } from "node:fs/promises";
|
|
20777
|
-
import { isAbsolute as
|
|
21080
|
+
import { isAbsolute as isAbsolute26 } from "node:path";
|
|
20778
21081
|
var API_VERSION36 = "^0.1.10";
|
|
20779
21082
|
var templates = /* @__PURE__ */ new Map();
|
|
20780
21083
|
var MAX_TEMPLATES = 256;
|
|
@@ -20830,7 +21133,7 @@ function renderTemplateRaw(template, variables) {
|
|
|
20830
21133
|
return result;
|
|
20831
21134
|
}
|
|
20832
21135
|
function validateRelativeTemplatePath(field, value) {
|
|
20833
|
-
if (
|
|
21136
|
+
if (isAbsolute26(value) || value.split(/[\\/]+/).includes("..")) {
|
|
20834
21137
|
return `${field} must be a relative path without ".." components`;
|
|
20835
21138
|
}
|
|
20836
21139
|
if (!withinProject(value)) {
|
|
@@ -21430,7 +21733,7 @@ var test_coverage_gate_default = plugin56;
|
|
|
21430
21733
|
import { execFile as execFile14 } from "node:child_process";
|
|
21431
21734
|
import { readFileSync as readFileSync15 } from "node:fs";
|
|
21432
21735
|
import { createRequire as createRequire3 } from "node:module";
|
|
21433
|
-
import { dirname as dirname8, isAbsolute as
|
|
21736
|
+
import { dirname as dirname8, isAbsolute as isAbsolute27, relative as relative26, resolve as resolve27 } from "node:path";
|
|
21434
21737
|
var API_VERSION38 = "^0.1.10";
|
|
21435
21738
|
var state52 = {
|
|
21436
21739
|
invocationCount: 0,
|
|
@@ -21499,17 +21802,17 @@ var ALLOWED_RUNNER_FLAGS = /* @__PURE__ */ new Set([
|
|
|
21499
21802
|
"--reporter=verbose",
|
|
21500
21803
|
"--reporter=default"
|
|
21501
21804
|
]);
|
|
21502
|
-
function
|
|
21805
|
+
function withinProject7(p) {
|
|
21503
21806
|
if (p.length === 0 || p.length > 4096 || p.startsWith("-")) return false;
|
|
21504
21807
|
const root = resolve27(process.cwd());
|
|
21505
|
-
const resolved =
|
|
21506
|
-
const rel =
|
|
21507
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
21808
|
+
const resolved = isAbsolute27(p) ? resolve27(p) : resolve27(root, p);
|
|
21809
|
+
const rel = relative26(root, resolved);
|
|
21810
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute27(rel);
|
|
21508
21811
|
}
|
|
21509
21812
|
function isInside3(parent, child) {
|
|
21510
21813
|
if (parent === child) return true;
|
|
21511
|
-
const rel =
|
|
21512
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
21814
|
+
const rel = relative26(parent, child);
|
|
21815
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute27(rel);
|
|
21513
21816
|
}
|
|
21514
21817
|
function tokenizeCommand(command) {
|
|
21515
21818
|
const trimmed = command.trim();
|
|
@@ -21553,7 +21856,7 @@ function resolveTestCommand(baseCommand, testPattern) {
|
|
|
21553
21856
|
if (!relativeBin) return null;
|
|
21554
21857
|
const packageDir = dirname8(packagePath);
|
|
21555
21858
|
const candidate = resolve27(packageDir, relativeBin);
|
|
21556
|
-
if (
|
|
21859
|
+
if (isAbsolute27(relativeBin) || !isInside3(packageDir, candidate)) {
|
|
21557
21860
|
return null;
|
|
21558
21861
|
}
|
|
21559
21862
|
resolvedEntry = candidate;
|
|
@@ -21562,7 +21865,7 @@ function resolveTestCommand(baseCommand, testPattern) {
|
|
|
21562
21865
|
}
|
|
21563
21866
|
const args = [resolvedEntry, ...runnerArgs];
|
|
21564
21867
|
if (testPattern) {
|
|
21565
|
-
if (!
|
|
21868
|
+
if (!withinProject7(testPattern)) return null;
|
|
21566
21869
|
args.push(testPattern);
|
|
21567
21870
|
}
|
|
21568
21871
|
return {
|
|
@@ -21796,7 +22099,7 @@ var test_flake_detector_default = plugin57;
|
|
|
21796
22099
|
|
|
21797
22100
|
// src/test-generator/index.ts
|
|
21798
22101
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
21799
|
-
import { isAbsolute as
|
|
22102
|
+
import { isAbsolute as isAbsolute28, relative as relative27, resolve as resolve28 } from "node:path";
|
|
21800
22103
|
var API_VERSION39 = "^0.1.10";
|
|
21801
22104
|
var state53 = {
|
|
21802
22105
|
generateCount: 0,
|
|
@@ -21852,21 +22155,21 @@ var SOURCE_EXTENSIONS = [
|
|
|
21852
22155
|
".cpp",
|
|
21853
22156
|
".hpp"
|
|
21854
22157
|
];
|
|
21855
|
-
function
|
|
22158
|
+
function withinProject8(p) {
|
|
21856
22159
|
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
21857
22160
|
const root = process.cwd();
|
|
21858
|
-
const resolved =
|
|
21859
|
-
const rel =
|
|
22161
|
+
const resolved = isAbsolute28(p) ? resolve28(p) : resolve28(root, p);
|
|
22162
|
+
const rel = relative27(root, resolved);
|
|
21860
22163
|
if (rel === "" || rel === ".") return true;
|
|
21861
22164
|
if (rel.startsWith("..")) return false;
|
|
21862
|
-
if (
|
|
22165
|
+
if (isAbsolute28(rel)) return false;
|
|
21863
22166
|
return true;
|
|
21864
22167
|
}
|
|
21865
22168
|
function toPosix8(p) {
|
|
21866
22169
|
return p.replace(/\\/g, "/");
|
|
21867
22170
|
}
|
|
21868
22171
|
function relativePath8(p) {
|
|
21869
|
-
return toPosix8(
|
|
22172
|
+
return toPosix8(relative27(process.cwd(), p));
|
|
21870
22173
|
}
|
|
21871
22174
|
function detectExports(content) {
|
|
21872
22175
|
const exports = [];
|
|
@@ -22076,7 +22379,7 @@ var plugin58 = {
|
|
|
22076
22379
|
if (!rawPath || typeof rawPath !== "string") {
|
|
22077
22380
|
return { ok: false, error: "path is required" };
|
|
22078
22381
|
}
|
|
22079
|
-
if (!
|
|
22382
|
+
if (!withinProject8(rawPath)) {
|
|
22080
22383
|
return { ok: false, error: "path is outside the project root" };
|
|
22081
22384
|
}
|
|
22082
22385
|
if (!SOURCE_EXTENSIONS.some((ext) => rawPath.toLowerCase().endsWith(ext))) {
|
|
@@ -22172,7 +22475,7 @@ var test_generator_default = plugin58;
|
|
|
22172
22475
|
// src/test-runner-gate/index.ts
|
|
22173
22476
|
import { execFile as execFile15 } from "node:child_process";
|
|
22174
22477
|
import { access as access5 } from "node:fs/promises";
|
|
22175
|
-
import { basename as basename7, dirname as dirname9, isAbsolute as
|
|
22478
|
+
import { basename as basename7, dirname as dirname9, isAbsolute as isAbsolute29, join as join8 } from "node:path";
|
|
22176
22479
|
import { buildWin32CmdShimInvocation as buildWin32CmdShimInvocation2, resolveWin32Command as resolveWin32Command2 } from "@wrongstack/tools/win32";
|
|
22177
22480
|
function resolveExec(command, args) {
|
|
22178
22481
|
const resolved = resolveWin32Command2(command);
|
|
@@ -22341,7 +22644,7 @@ function resolveAllowedCommand2(customCommand) {
|
|
|
22341
22644
|
if (ALLOWED_COMMAND_TOKENS.has(head)) {
|
|
22342
22645
|
return { cmd: head, args: tokens.slice(1) };
|
|
22343
22646
|
}
|
|
22344
|
-
if (
|
|
22647
|
+
if (isAbsolute29(head)) {
|
|
22345
22648
|
if (!withinProject(head)) return null;
|
|
22346
22649
|
const base = basename7(head);
|
|
22347
22650
|
if (ALLOWED_COMMAND_TOKENS.has(base)) {
|