pushary 1.9.24 → 1.9.25
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/CHANGELOG.md +8 -0
- package/dist/bin/pushary-bell-hook.js +25 -25
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +24 -24
- package/dist/bin/pushary-clean.js +29 -29
- package/dist/bin/pushary-codex-bridge.js +17 -17
- package/dist/bin/pushary-codex-hook.js +30 -30
- package/dist/bin/pushary-codex.js +12 -12
- package/dist/bin/pushary-connect.js +24 -24
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +31 -31
- package/dist/bin/pushary-disconnect.js +24 -24
- package/dist/bin/pushary-doctor.js +33 -33
- package/dist/bin/pushary-elicitation-hook.js +25 -25
- package/dist/bin/pushary-gemini-bridge.js +13 -13
- package/dist/bin/pushary-gemini-hook.js +27 -27
- package/dist/bin/pushary-hook.js +29 -29
- package/dist/bin/pushary-login.js +12 -12
- package/dist/bin/pushary-logout.js +24 -24
- package/dist/bin/pushary-mcp.js +9 -9
- package/dist/bin/pushary-mode.js +12 -12
- package/dist/bin/pushary-notification-hook.js +24 -24
- package/dist/bin/pushary-opencode-hook.js +26 -26
- package/dist/bin/pushary-permission-denied-hook.js +29 -29
- package/dist/bin/pushary-permission-hook.js +28 -28
- package/dist/bin/pushary-post-hook.js +24 -24
- package/dist/bin/pushary-prompt-hook.js +24 -24
- package/dist/bin/pushary-session-end-hook.js +24 -24
- package/dist/bin/pushary-session-start-hook.js +28 -28
- package/dist/bin/pushary-setup.js +35 -35
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +26 -26
- package/dist/bin/pushary-stop-hook.js +24 -24
- package/dist/bin/pushary-stopfailure-hook.js +24 -24
- package/dist/bin/pushary-suggestions.js +12 -12
- package/dist/bin/pushary-transcript-register.js +17 -17
- package/dist/bin/pushary-transcripts.js +7 -7
- package/dist/bin/pushary-upgrade.js +30 -30
- package/dist/bin/pushary-wait.js +12 -12
- package/dist/bin/pushary.js +6 -6
- package/dist/{chunk-GBD6G4TA.js → chunk-2CUE3XXS.js} +1 -1
- package/dist/{chunk-T5KM7J75.js → chunk-2J2WFX7X.js} +4 -4
- package/dist/{chunk-NQICO3ES.js → chunk-2VQZC6DW.js} +4 -4
- package/dist/{chunk-G6GFNKF7.js → chunk-2XHBYMVN.js} +11 -11
- package/dist/{chunk-7JHYWFY6.js → chunk-3UYMOWX6.js} +2 -2
- package/dist/{chunk-NJK4MGXL.js → chunk-4KZP4YMV.js} +1 -1
- package/dist/{chunk-T6TJA4RF.js → chunk-5ETE7F45.js} +2 -2
- package/dist/{chunk-OY6NJWDD.js → chunk-5Q2QJ2AL.js} +1 -1
- package/dist/{chunk-LOERK6N3.js → chunk-5WTEKIAS.js} +4 -4
- package/dist/{chunk-W5NHIA4Y.js → chunk-7UZD2F4Y.js} +8 -8
- package/dist/{chunk-RK7TDMQT.js → chunk-7V6PJFDF.js} +3 -3
- package/dist/{chunk-RFK2WN6S.js → chunk-A3QQWXIE.js} +1 -1
- package/dist/{chunk-KT5BH6GC.js → chunk-AXB4ABUD.js} +8 -8
- package/dist/{chunk-EKPAV33G.js → chunk-BGJDOHNF.js} +5 -5
- package/dist/{chunk-CLVU24OZ.js → chunk-BHRMHDOO.js} +2 -2
- package/dist/{chunk-Q7IEFI7V.js → chunk-EJJNZYDS.js} +2 -2
- package/dist/{chunk-GXQVOKHJ.js → chunk-EJMXZH5E.js} +2 -2
- package/dist/{chunk-RIBIEQHU.js → chunk-FCXYDR62.js} +1 -1
- package/dist/{chunk-O3RRQTTV.js → chunk-FJKV64M7.js} +5 -5
- package/dist/{chunk-3WRJU7KN.js → chunk-FLC4TVXH.js} +1 -1
- package/dist/{chunk-7Z2DLAEE.js → chunk-G446AVR5.js} +2 -2
- package/dist/{chunk-U23XTMBF.js → chunk-GC5HJE3T.js} +3 -3
- package/dist/{chunk-35U2QHH2.js → chunk-HEJMZVJI.js} +2 -2
- package/dist/{chunk-GM2IFDVF.js → chunk-KGSN2SOL.js} +769 -17
- package/dist/{chunk-VFIZFKZY.js → chunk-KM7AEGKF.js} +3 -3
- package/dist/{chunk-264EOGIK.js → chunk-KP4BIZLP.js} +2 -2
- package/dist/{chunk-XA2RGGNR.js → chunk-KZLOVLA6.js} +4 -4
- package/dist/{chunk-SH2KK2ED.js → chunk-LDNKGG5G.js} +3 -3
- package/dist/{chunk-SBQCRQIP.js → chunk-N3ACK3YA.js} +4 -4
- package/dist/{chunk-ZVON5PCF.js → chunk-NFH2H7PD.js} +5 -5
- package/dist/{chunk-URCSJZNU.js → chunk-OFHSTCXK.js} +3 -3
- package/dist/{chunk-QDKNLNCA.js → chunk-OOKIWOMK.js} +1 -1
- package/dist/{chunk-KX776UXN.js → chunk-OWOSNGEQ.js} +2 -2
- package/dist/{chunk-HBR6ZZHN.js → chunk-PAMW5M53.js} +5 -5
- package/dist/{chunk-Z5L4NQHC.js → chunk-PM2WJSUH.js} +1 -1
- package/dist/{chunk-WYLA6J43.js → chunk-PPGLIWOR.js} +1 -1
- package/dist/{chunk-OLWKCDK4.js → chunk-PSNF27BC.js} +6 -6
- package/dist/{chunk-HLXGEOX3.js → chunk-Q37XFSPO.js} +4 -4
- package/dist/{chunk-EE357RIP.js → chunk-RSENVGBQ.js} +4 -4
- package/dist/{chunk-E4B7TXVS.js → chunk-SGGR5MN2.js} +1 -1
- package/dist/{chunk-P447F2RB.js → chunk-TLUSUDRU.js} +1 -1
- package/dist/{chunk-L5MCJNSI.js → chunk-UJB6HHP4.js} +5 -5
- package/dist/{chunk-2F6UQ3YD.js → chunk-UK7OY3EJ.js} +1 -1
- package/dist/{chunk-IO5ZDIHY.js → chunk-UUOC7A7M.js} +7 -7
- package/dist/{chunk-H2RD5EMR.js → chunk-VEGS4NYF.js} +3 -3
- package/dist/{chunk-XQROISQB.js → chunk-VFF7ZBIS.js} +2 -2
- package/dist/{chunk-LHFK5F3T.js → chunk-VN5Q4KOD.js} +2 -2
- package/dist/{chunk-2JTJZ7VI.js → chunk-WEKXYS2F.js} +4 -4
- package/dist/{chunk-CYGIJXAP.js → chunk-WKEP4JAW.js} +2 -2
- package/dist/{chunk-JDWOK6PE.js → chunk-YD3DIFIU.js} +1 -1
- package/dist/{chunk-IS737HST.js → chunk-ZLGCNHJG.js} +1 -1
- package/dist/{chunk-OSOCONIE.js → chunk-ZLY7BHN4.js} +5 -5
- package/dist/{chunk-VICG46JC.js → chunk-ZNT4ZZXJ.js} +1 -1
- package/dist/{chunk-2E6AWHV7.js → chunk-ZV3262AA.js} +3 -3
- package/dist/{global-upgrade-DZGLAARJ.js → global-upgrade-4M4TJKQR.js} +6 -6
- package/dist/{paths-JATF66FP.js → paths-6JY6J56N.js} +2 -2
- package/dist/{reapply-7TDCFXNF.js → reapply-LFEQ5GF6.js} +20 -20
- package/dist/src/index.js +19 -19
- package/dist/{upgrade-lock-LPIDFQEH.js → upgrade-lock-CZRCHYVU.js} +3 -3
- package/package.json +1 -1
|
@@ -708,12 +708,14 @@ var scanShell = (command) => {
|
|
|
708
708
|
}
|
|
709
709
|
return quote === null ? lexemes : null;
|
|
710
710
|
};
|
|
711
|
-
var
|
|
711
|
+
var PIPE = "|";
|
|
712
|
+
var splitShellSegments = (command) => {
|
|
712
713
|
const lexemes = scanShell(command);
|
|
713
714
|
if (lexemes === null) return null;
|
|
714
715
|
const segments = [];
|
|
715
716
|
let start = 0;
|
|
716
717
|
let consumed = -1;
|
|
718
|
+
let readsPipe = false;
|
|
717
719
|
for (const lexeme of lexemes) {
|
|
718
720
|
const { index, text, quote, kind } = lexeme;
|
|
719
721
|
if (index <= consumed || kind !== "text" || quote === "'") continue;
|
|
@@ -721,14 +723,103 @@ var shellCommandSegments = (command) => {
|
|
|
721
723
|
if (quote !== null) continue;
|
|
722
724
|
if (UNDECOMPOSABLE.has(text)) return null;
|
|
723
725
|
if (!SEGMENT_SEPARATORS.has(text)) continue;
|
|
724
|
-
|
|
725
|
-
|
|
726
|
+
const doubled = DOUBLED_SEPARATORS.has(text) && command[index + 1] === text;
|
|
727
|
+
consumed = index + (doubled ? 1 : 0);
|
|
728
|
+
segments.push({ text: trimShellWhitespace(command.slice(start, index)), readsPipe });
|
|
729
|
+
readsPipe = text === PIPE && !doubled;
|
|
726
730
|
start = consumed + 1;
|
|
727
731
|
}
|
|
728
|
-
segments.push(trimShellWhitespace(command.slice(start)));
|
|
729
|
-
const present = segments.filter((segment) => segment.length > 0);
|
|
732
|
+
segments.push({ text: trimShellWhitespace(command.slice(start)), readsPipe });
|
|
733
|
+
const present = segments.filter((segment) => segment.text.length > 0);
|
|
730
734
|
return present.length > 0 ? present : null;
|
|
731
735
|
};
|
|
736
|
+
var shellCommandSegments = (command) => splitShellSegments(command)?.map((segment) => segment.text) ?? null;
|
|
737
|
+
var shellSegmentsReadingAPipe = (command) => splitShellSegments(command)?.filter((segment) => segment.readsPipe).map((segment) => segment.text) ?? null;
|
|
738
|
+
var SUBSTITUTION_OPEN = "$(";
|
|
739
|
+
var QUOTED_HEREDOC_CAT = /^[ \t]*cat[ \t]+<<[ \t]*(?:'([A-Za-z_][A-Za-z0-9_]*)'|"([A-Za-z_][A-Za-z0-9_]*)")[ \t]*\n/;
|
|
740
|
+
var CLOSING_PARENTHESIS = /^\s*\)/;
|
|
741
|
+
var LEADING_TABS = /^\t+/;
|
|
742
|
+
var CHARACTERS_ESCAPED_IN_DOUBLE_QUOTES = /["\\$`]/g;
|
|
743
|
+
var isHeredocTerminator = (line, delimiter) => line.replace(LEADING_TABS, "").trimEnd() === delimiter;
|
|
744
|
+
var mightEndAHeredoc = (line, delimiter) => line.trimStart().startsWith(delimiter);
|
|
745
|
+
var BODY_ESCAPES = /[\\`]/;
|
|
746
|
+
var COMMENT_START = "#";
|
|
747
|
+
var WHITESPACE = /\s/;
|
|
748
|
+
var parenthesesBalanceInBody = (body, readsComments) => {
|
|
749
|
+
let depth = 0;
|
|
750
|
+
let quote = null;
|
|
751
|
+
let comment = false;
|
|
752
|
+
let wordStart = true;
|
|
753
|
+
for (const char of body) {
|
|
754
|
+
if (comment) {
|
|
755
|
+
comment = char !== "\n";
|
|
756
|
+
wordStart = true;
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
if (quote !== null) {
|
|
760
|
+
if (char === quote) quote = null;
|
|
761
|
+
wordStart = false;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (readsComments && wordStart && char === COMMENT_START) {
|
|
765
|
+
comment = true;
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (char === "'" || char === '"') quote = char;
|
|
769
|
+
if (char === "(") depth += 1;
|
|
770
|
+
if (char === ")") depth -= 1;
|
|
771
|
+
if (depth < 0) return false;
|
|
772
|
+
wordStart = WHITESPACE.test(char);
|
|
773
|
+
}
|
|
774
|
+
return depth === 0 && quote === null;
|
|
775
|
+
};
|
|
776
|
+
var everyBashReadsTheBodyAsText = (body) => !BODY_ESCAPES.test(body) && parenthesesBalanceInBody(body, true) && parenthesesBalanceInBody(body, false);
|
|
777
|
+
var quotedHeredocSubstitutionAt = (command, open) => {
|
|
778
|
+
const header = QUOTED_HEREDOC_CAT.exec(command.slice(open + SUBSTITUTION_OPEN.length));
|
|
779
|
+
const delimiter = header?.[1] ?? header?.[2];
|
|
780
|
+
if (!header || !delimiter) return null;
|
|
781
|
+
const bodyStart = open + SUBSTITUTION_OPEN.length + header[0].length;
|
|
782
|
+
for (let lineStart = bodyStart; lineStart <= command.length; ) {
|
|
783
|
+
const newline = command.indexOf("\n", lineStart);
|
|
784
|
+
const lineEnd = newline === -1 ? command.length : newline;
|
|
785
|
+
const line = command.slice(lineStart, lineEnd);
|
|
786
|
+
if (line === delimiter) {
|
|
787
|
+
const closing = CLOSING_PARENTHESIS.exec(command.slice(lineEnd));
|
|
788
|
+
const body = command.slice(bodyStart, Math.max(bodyStart, lineStart - 1));
|
|
789
|
+
return closing && everyBashReadsTheBodyAsText(body) ? { body, end: lineEnd + closing[0].length } : null;
|
|
790
|
+
}
|
|
791
|
+
if (mightEndAHeredoc(line, delimiter)) return null;
|
|
792
|
+
if (newline === -1) return null;
|
|
793
|
+
lineStart = newline + 1;
|
|
794
|
+
}
|
|
795
|
+
return null;
|
|
796
|
+
};
|
|
797
|
+
var asDoubleQuotedText = (body) => body.replace(CHARACTERS_ESCAPED_IN_DOUBLE_QUOTES, (character) => `\\${character}`);
|
|
798
|
+
var withHeredocSubstitutionsAsQuotedText = (command) => {
|
|
799
|
+
let result = "";
|
|
800
|
+
let copied = 0;
|
|
801
|
+
let quote = null;
|
|
802
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
803
|
+
const char = command.charAt(index);
|
|
804
|
+
if (char === "\\" && quote !== "'") {
|
|
805
|
+
index += 1;
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if ((char === "'" || char === '"') && (quote === null || quote === char)) {
|
|
809
|
+
quote = quote === null ? char : null;
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (quote === "'") continue;
|
|
813
|
+
if (char === "`") break;
|
|
814
|
+
if (char !== "$" || command.charAt(index + 1) !== "(") continue;
|
|
815
|
+
const substitution = quote === '"' ? quotedHeredocSubstitutionAt(command, index) : null;
|
|
816
|
+
if (substitution === null) break;
|
|
817
|
+
result += command.slice(copied, index) + asDoubleQuotedText(substitution.body);
|
|
818
|
+
copied = substitution.end;
|
|
819
|
+
index = substitution.end - 1;
|
|
820
|
+
}
|
|
821
|
+
return result + command.slice(copied);
|
|
822
|
+
};
|
|
732
823
|
var shellVisibleText = (command) => {
|
|
733
824
|
const lexemes = scanShell(command);
|
|
734
825
|
if (lexemes === null) return null;
|
|
@@ -1183,11 +1274,12 @@ var PATTERN_INTENT = {
|
|
|
1183
1274
|
grant: "grant",
|
|
1184
1275
|
restrict: "restrict"
|
|
1185
1276
|
};
|
|
1277
|
+
var segmentsForMatching = (command) => shellCommandSegments(withHeredocSubstitutionsAsQuotedText(command));
|
|
1186
1278
|
var everySegmentMatches = (command, matches) => {
|
|
1187
|
-
const segments =
|
|
1279
|
+
const segments = segmentsForMatching(command);
|
|
1188
1280
|
return segments !== null && segments.every(matches);
|
|
1189
1281
|
};
|
|
1190
|
-
var anySegmentMatches = (command, matches) => (
|
|
1282
|
+
var anySegmentMatches = (command, matches) => (segmentsForMatching(command) ?? [command]).some(matches);
|
|
1191
1283
|
var shellSegmentsMatch = (command, intent, matches) => intent === PATTERN_INTENT.restrict ? anySegmentMatches(command, matches) : everySegmentMatches(command, matches);
|
|
1192
1284
|
var rankWholeOrSegments = (toolName, arg, intent, matches) => {
|
|
1193
1285
|
const matched = toolName in POLICY_COMMAND_KEYS ? shellSegmentsMatch(arg, intent, matches) : matches(arg);
|
|
@@ -1646,7 +1738,9 @@ var INLINE_CODE_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
|
1646
1738
|
"lua",
|
|
1647
1739
|
"osascript",
|
|
1648
1740
|
"pwsh",
|
|
1649
|
-
"powershell"
|
|
1741
|
+
"powershell",
|
|
1742
|
+
"mongosh",
|
|
1743
|
+
"mongo"
|
|
1650
1744
|
]);
|
|
1651
1745
|
var INLINE_CODE_LONG_FLAG = /^-{1,2}(?:eval|print|require|import|command|encodedcommand|enc|ec)(?:=|$)/i;
|
|
1652
1746
|
var INLINE_CODE_SHORT_FLAG_LETTERS = /* @__PURE__ */ new Set(["c", "e", "E", "p", "r"]);
|
|
@@ -1669,7 +1763,15 @@ var DESTRUCTIVE_ARGUMENTS_BY_COMMAND = /* @__PURE__ */ new Map([
|
|
|
1669
1763
|
])],
|
|
1670
1764
|
["git checkout", /* @__PURE__ */ new Set(["-f", "--force", "--", "-B"])],
|
|
1671
1765
|
["git switch", /* @__PURE__ */ new Set(["-f", "--force", "--discard-changes", "-C", "--force-create"])],
|
|
1672
|
-
["git branch", /* @__PURE__ */ new Set(["-f", "--force", "-D", "-M", "-C"])]
|
|
1766
|
+
["git branch", /* @__PURE__ */ new Set(["-f", "--force", "-D", "-M", "-C"])],
|
|
1767
|
+
["git push", /* @__PURE__ */ new Set(["-f", "--force", "--force-with-lease", "--force-if-includes", "--mirror", "-d", "--delete", "--prune"])],
|
|
1768
|
+
["git clean", /* @__PURE__ */ new Set(["-f", "--force"])],
|
|
1769
|
+
["rm", /* @__PURE__ */ new Set(["-r", "-R", "--recursive"])],
|
|
1770
|
+
["pg_restore", /* @__PURE__ */ new Set(["-c", "--clean"])],
|
|
1771
|
+
["mongorestore", /* @__PURE__ */ new Set(["--drop"])],
|
|
1772
|
+
["aws s3", /* @__PURE__ */ new Set(["--delete"])],
|
|
1773
|
+
["gsutil rsync", /* @__PURE__ */ new Set(["-d", "--delete-unmatched-destination-objects"])],
|
|
1774
|
+
["gcloud storage", /* @__PURE__ */ new Set(["--delete-unmatched-destination-objects"])]
|
|
1673
1775
|
]);
|
|
1674
1776
|
var DESTRUCTIVE_WORDS_IN_ANY_CASE_BY_COMMAND = /* @__PURE__ */ new Map([
|
|
1675
1777
|
["redis-cli", /* @__PURE__ */ new Set(["flushall", "flushdb"])]
|
|
@@ -1710,7 +1812,8 @@ var SUBCOMMAND_CLIS = /* @__PURE__ */ new Set([
|
|
|
1710
1812
|
"heroku",
|
|
1711
1813
|
"supabase",
|
|
1712
1814
|
"wrangler",
|
|
1713
|
-
"railway"
|
|
1815
|
+
"railway",
|
|
1816
|
+
"bq"
|
|
1714
1817
|
]);
|
|
1715
1818
|
var DESTRUCTIVE_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
1716
1819
|
"delete",
|
|
@@ -1812,33 +1915,681 @@ var argumentsHideTheEffect = (name, args) => {
|
|
|
1812
1915
|
return (meaningful[0]?.text.startsWith("-") ?? false) || subcommandWordsOf(meaningful).some((word) => word.expands || RUNNER_SUBCOMMANDS.has(word.text));
|
|
1813
1916
|
};
|
|
1814
1917
|
var invocationHidesItsEffect = ({ program, args }) => program !== void 0 && ([program, ...args].some((word) => word.comment) || programHidesItsEffect(program) || argumentsHideTheEffect(basenameOf(program.text), args));
|
|
1815
|
-
var
|
|
1816
|
-
var
|
|
1918
|
+
var commandForReading = (command) => withHeredocSubstitutionsAsQuotedText(command).replace(SHELL_LINE_CONTINUATION, "").replace(SAFE_REDIRECTION, "");
|
|
1919
|
+
var readableCommandSegments = (command) => shellCommandSegments(commandForReading(command));
|
|
1920
|
+
var invocationTextWith = ({ program, args }, wordText) => {
|
|
1817
1921
|
if (program === void 0) return "";
|
|
1818
1922
|
const name = basenameOf(program.text);
|
|
1819
1923
|
const kept = SUBCOMMAND_CLIS.has(name) ? argsAfterHarmlessGlobalFlags(args) : args;
|
|
1820
|
-
return [name, ...kept.map(
|
|
1821
|
-
};
|
|
1924
|
+
return [name, ...kept.map(wordText)].join(" ");
|
|
1925
|
+
};
|
|
1926
|
+
var normalizedInvocation = (invocation) => invocationTextWith(invocation, (word) => word.text);
|
|
1927
|
+
var SHELL_METACHARACTER = /[\s;&|<>()$`\\"'*?[\]#~{}!]/;
|
|
1928
|
+
var SINGLE_QUOTE = "'";
|
|
1929
|
+
var ESCAPED_SINGLE_QUOTE = `'\\''`;
|
|
1930
|
+
var asOneShellWord = (text) => SHELL_METACHARACTER.test(text) ? `${SINGLE_QUOTE}${text.replaceAll(SINGLE_QUOTE, ESCAPED_SINGLE_QUOTE)}${SINGLE_QUOTE}` : text;
|
|
1931
|
+
var quotedInvocation = (invocation) => invocationTextWith(invocation, (word) => asOneShellWord(word.text));
|
|
1822
1932
|
var normalizedCommand = (command) => {
|
|
1823
1933
|
const segments = readableCommandSegments(command);
|
|
1824
1934
|
return segments === null ? null : segments.map((segment) => normalizedInvocation(invocationOf(segment))).join("; ");
|
|
1825
1935
|
};
|
|
1826
1936
|
var subcommandDestroys = ({ program, args }) => program !== void 0 && SUBCOMMAND_CLIS.has(basenameOf(program.text)) && subcommandWordsOf(argsAfterHarmlessGlobalFlags(args)).some((word) => subcommandWordDestroys(word.text));
|
|
1937
|
+
var wordsOfInvocation = ({ program, args }) => program === void 0 ? args : [program, ...args];
|
|
1938
|
+
var PRODUCTION_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
|
|
1939
|
+
var PRODUCTION_FLAGS = /* @__PURE__ */ new Set(["--prod", "--production"]);
|
|
1940
|
+
var ENVIRONMENT_FLAGS = /* @__PURE__ */ new Set([
|
|
1941
|
+
"-e",
|
|
1942
|
+
"--env",
|
|
1943
|
+
"--environment",
|
|
1944
|
+
"--stage",
|
|
1945
|
+
"--profile",
|
|
1946
|
+
"--context",
|
|
1947
|
+
"--target"
|
|
1948
|
+
]);
|
|
1949
|
+
var ENVIRONMENT_VARIABLE = /^[A-Z_]*(?:ENV|ENVIRONMENT|STAGE)$/;
|
|
1950
|
+
var FLAG_VALUE_SEPARATOR = "=";
|
|
1951
|
+
var DEPLOY_CLIS = /* @__PURE__ */ new Set([
|
|
1952
|
+
"vercel",
|
|
1953
|
+
"netlify",
|
|
1954
|
+
"fly",
|
|
1955
|
+
"flyctl",
|
|
1956
|
+
"heroku",
|
|
1957
|
+
"railway",
|
|
1958
|
+
"firebase",
|
|
1959
|
+
"wrangler",
|
|
1960
|
+
"supabase",
|
|
1961
|
+
"serverless",
|
|
1962
|
+
"sls"
|
|
1963
|
+
]);
|
|
1964
|
+
var SCHEMA_CHANGE_WORD = /(?:^|[\s:_./-])(?:migrat(?:e|es|ion|ions)|rollback|seed|db[:\s]push)(?=$|[\s:_./-])(?![\s:_./-]+(?:status|version|list|ls|show|check|diff|history|info)(?:$|[\s:_./-]))/i;
|
|
1965
|
+
var PRODUCTION_READ_VERBS = /* @__PURE__ */ new Set([
|
|
1966
|
+
"logs",
|
|
1967
|
+
"log",
|
|
1968
|
+
"tail",
|
|
1969
|
+
"status",
|
|
1970
|
+
"list",
|
|
1971
|
+
"ls",
|
|
1972
|
+
"info",
|
|
1973
|
+
"inspect",
|
|
1974
|
+
"describe",
|
|
1975
|
+
"get",
|
|
1976
|
+
"show",
|
|
1977
|
+
"whoami",
|
|
1978
|
+
"build",
|
|
1979
|
+
"dev",
|
|
1980
|
+
"open",
|
|
1981
|
+
"history",
|
|
1982
|
+
"metrics",
|
|
1983
|
+
"help",
|
|
1984
|
+
"version",
|
|
1985
|
+
"login",
|
|
1986
|
+
"link",
|
|
1987
|
+
"pull"
|
|
1988
|
+
]);
|
|
1989
|
+
var PRODUCTION_SETTING_NAMESPACES = /* @__PURE__ */ new Set(["env", "secret", "secrets", "config", "domains", "dns", "certs"]);
|
|
1990
|
+
var deployCliOperandsAfter = (words, cli) => {
|
|
1991
|
+
const after = words.slice(cli + 1);
|
|
1992
|
+
const firstFlag = after.findIndex((word) => FLAG.test(word.text));
|
|
1993
|
+
return (firstFlag === -1 ? after : after.slice(0, firstFlag)).map((word) => word.text.toLowerCase());
|
|
1994
|
+
};
|
|
1995
|
+
var deployCliOnlyReads = (operands) => {
|
|
1996
|
+
const [first, second] = operands.map((operand) => operand.split(":")[0] ?? "");
|
|
1997
|
+
const verb = PRODUCTION_SETTING_NAMESPACES.has(first ?? "") ? second : first;
|
|
1998
|
+
return verb !== void 0 && PRODUCTION_READ_VERBS.has(verb);
|
|
1999
|
+
};
|
|
2000
|
+
var deployCliChangesSomething = (words) => words.some((word, index) => DEPLOY_CLIS.has(basenameOf(word.text)) && !deployCliOnlyReads(deployCliOperandsAfter(words, index)));
|
|
2001
|
+
var valueSelectsProduction = (name, value, names) => names.has(name) && PRODUCTION_NAMES.has(value.toLowerCase());
|
|
2002
|
+
var wordSelectsProduction = (word, previous) => {
|
|
2003
|
+
const separator = word.text.indexOf(FLAG_VALUE_SEPARATOR);
|
|
2004
|
+
const name = separator > 0 ? word.text.slice(0, separator) : "";
|
|
2005
|
+
const value = separator > 0 ? word.text.slice(separator + 1) : "";
|
|
2006
|
+
return PRODUCTION_FLAGS.has(word.text.toLowerCase()) || valueSelectsProduction(name.toLowerCase(), value, ENVIRONMENT_FLAGS) || ENVIRONMENT_VARIABLE.test(name) && PRODUCTION_NAMES.has(value.toLowerCase()) || valueSelectsProduction((previous?.text ?? "").toLowerCase(), word.text, ENVIRONMENT_FLAGS);
|
|
2007
|
+
};
|
|
2008
|
+
var changesProduction = (invocation) => {
|
|
2009
|
+
const words = wordsOfInvocation(invocation);
|
|
2010
|
+
return words.some((word, index) => wordSelectsProduction(word, words[index - 1])) && (deployCliChangesSomething(words) || SCHEMA_CHANGE_WORD.test(normalizedInvocation(invocation)));
|
|
2011
|
+
};
|
|
2012
|
+
var MYSQL_SYNTAX = { inlineFlags: /* @__PURE__ */ new Set(["-e", "--execute"]), scriptFlags: /* @__PURE__ */ new Set(), positionalSql: false };
|
|
2013
|
+
var SQLITE_SYNTAX = { inlineFlags: /* @__PURE__ */ new Set(["-cmd"]), scriptFlags: /* @__PURE__ */ new Set(["-init"]), positionalSql: true };
|
|
2014
|
+
var SQL_CLIENT_SYNTAX = /* @__PURE__ */ new Map([
|
|
2015
|
+
["psql", { inlineFlags: /* @__PURE__ */ new Set(["-c", "--command"]), scriptFlags: /* @__PURE__ */ new Set(["-f", "--file"]), positionalSql: false }],
|
|
2016
|
+
["mysql", MYSQL_SYNTAX],
|
|
2017
|
+
["mariadb", MYSQL_SYNTAX],
|
|
2018
|
+
["sqlite3", SQLITE_SYNTAX],
|
|
2019
|
+
["sqlite", SQLITE_SYNTAX],
|
|
2020
|
+
["duckdb", { inlineFlags: /* @__PURE__ */ new Set(["-c", "-s", "-cmd"]), scriptFlags: /* @__PURE__ */ new Set(["-f", "-init"]), positionalSql: true }],
|
|
2021
|
+
["clickhouse-client", { inlineFlags: /* @__PURE__ */ new Set(["-q", "--query"]), scriptFlags: /* @__PURE__ */ new Set(["--queries-file"]), positionalSql: false }],
|
|
2022
|
+
["bq", { inlineFlags: /* @__PURE__ */ new Set(), scriptFlags: /* @__PURE__ */ new Set(), positionalSql: true }],
|
|
2023
|
+
["mongosh", { inlineFlags: /* @__PURE__ */ new Set(["--eval"]), scriptFlags: /* @__PURE__ */ new Set(["-f", "--file"]), positionalSql: false }],
|
|
2024
|
+
["mongo", { inlineFlags: /* @__PURE__ */ new Set(["--eval"]), scriptFlags: /* @__PURE__ */ new Set(), positionalSql: false }]
|
|
2025
|
+
]);
|
|
2026
|
+
var SQL_WRITE_STATEMENT = new RegExp([
|
|
2027
|
+
String.raw`\b(?:insert|update|delete|merge|upsert|alter|create|drop|truncate|grant|revoke|copy|vacuum|reindex|rename|attach|detach)\b`,
|
|
2028
|
+
String.raw`\breplace\s+into\b`,
|
|
2029
|
+
String.raw`\bload\s+data\b`,
|
|
2030
|
+
String.raw`\binto\s+(?:out|dump)file\b`,
|
|
2031
|
+
String.raw`\bset\s+(?:global|persist)\b`,
|
|
2032
|
+
String.raw`\bkill\s+(?:query|connection|\d)`,
|
|
2033
|
+
String.raw`\bcall\s+[\w.]+\s*\(`,
|
|
2034
|
+
String.raw`\bdo\s+(?:\$\w*\$|')`,
|
|
2035
|
+
String.raw`\bpg_terminate_backend\b`,
|
|
2036
|
+
String.raw`\bsetval\b`,
|
|
2037
|
+
String.raw`\bpragma\s+writable_schema\b`,
|
|
2038
|
+
String.raw`\.(?:insert|update|delete|replace|remove|drop|rename|create|bulkwrite|findoneand)\w*\s*\(`
|
|
2039
|
+
].join("|"), "i");
|
|
2040
|
+
var SQL_READ_PHRASE = /\bshow\s+create\b/gi;
|
|
2041
|
+
var SQL_STRING_LITERAL = /'(?:[^']|'')*'/g;
|
|
2042
|
+
var EMPTY_SQL_STRING = "''";
|
|
2043
|
+
var SQL_COMMENT = /--[^\n]*|\/\*[\s\S]*?\*\//g;
|
|
2044
|
+
var writesThroughSql = (sql) => {
|
|
2045
|
+
const statement = sql.replace(SQL_COMMENT, " ").replace(SQL_STRING_LITERAL, EMPTY_SQL_STRING).replace(SQL_READ_PHRASE, "");
|
|
2046
|
+
return SQL_WRITE_STATEMENT.test(statement) || SQL_CLIENT_FILE_COMMAND.test(statement);
|
|
2047
|
+
};
|
|
2048
|
+
var SQL_CLIENT_FILE_COMMAND = new RegExp([
|
|
2049
|
+
String.raw`(?:^|[\s;=])\\(?:i|ir|include|include_relative|o|out|w|write|gexec|!|\.)(?=\s|$)`,
|
|
2050
|
+
String.raw`(?:^|[\s;=])\\g\s*\|`,
|
|
2051
|
+
String.raw`(?:^|[\s;=])\.(?:read|import|restore|load|output|once|shell|system|backup|save|open)(?=\s|$)`,
|
|
2052
|
+
String.raw`(?:^|[;=])\s*(?:system|source)\s`
|
|
2053
|
+
].join("|"), "i");
|
|
2054
|
+
var SHORT_FLAG_CLUSTER_ONLY = /^-([A-Za-z0-9]+)$/;
|
|
2055
|
+
var NO_SQL = { script: false, sql: null, hidden: false };
|
|
2056
|
+
var shortFlagOf = (flags, text) => [...flags].find((flag) => /^-[A-Za-z]$/.test(flag) && text.startsWith(flag) && text.length > flag.length && !text.startsWith("--"));
|
|
2057
|
+
var sqlArgumentAt = (args, index, syntax) => {
|
|
2058
|
+
const arg = args[index];
|
|
2059
|
+
if (arg === void 0) return NO_SQL;
|
|
2060
|
+
const next = args[index + 1];
|
|
2061
|
+
const valueOf = (word) => ({ script: false, sql: word?.text ?? null, hidden: word?.expands ?? false });
|
|
2062
|
+
if (syntax.scriptFlags.has(arg.text) || namesALongFlagValue(arg.text, syntax.scriptFlags) || shortFlagOf(syntax.scriptFlags, arg.text)) {
|
|
2063
|
+
return { script: true, sql: null, hidden: false };
|
|
2064
|
+
}
|
|
2065
|
+
if (syntax.inlineFlags.has(arg.text)) return valueOf(next);
|
|
2066
|
+
const longInline = [...syntax.inlineFlags].find((flag) => arg.text.startsWith(`${flag}${FLAG_VALUE_SEPARATOR}`));
|
|
2067
|
+
if (longInline) return { script: false, sql: arg.text.slice(longInline.length + 1), hidden: arg.expands };
|
|
2068
|
+
const shortInline = shortFlagOf(syntax.inlineFlags, arg.text);
|
|
2069
|
+
if (shortInline) return { script: false, sql: arg.text.slice(shortInline.length), hidden: arg.expands };
|
|
2070
|
+
const cluster = SHORT_FLAG_CLUSTER_ONLY.exec(arg.text)?.[1];
|
|
2071
|
+
const last = cluster ? `-${cluster.slice(-1)}` : "";
|
|
2072
|
+
if (cluster && cluster.length > 1 && syntax.scriptFlags.has(last)) return { script: true, sql: null, hidden: false };
|
|
2073
|
+
if (cluster && cluster.length > 1 && syntax.inlineFlags.has(last)) return valueOf(next);
|
|
2074
|
+
return { script: false, sql: arg.text, hidden: false };
|
|
2075
|
+
};
|
|
2076
|
+
var namesALongFlagValue = (text, flags) => [...flags].some((flag) => flag.startsWith("--") && text.startsWith(`${flag}${FLAG_VALUE_SEPARATOR}`));
|
|
2077
|
+
var positionalSqlIsHidden = (args, syntax) => syntax.positionalSql && args.filter((arg) => !arg.text.startsWith("-")).slice(1).some((arg) => arg.expands);
|
|
2078
|
+
var sqlClientSyntaxOf = ({ program }) => program === void 0 ? void 0 : SQL_CLIENT_SYNTAX.get(basenameOf(program.text));
|
|
2079
|
+
var sqlClientChangesData = (invocation) => {
|
|
2080
|
+
const syntax = sqlClientSyntaxOf(invocation);
|
|
2081
|
+
if (syntax === void 0) return false;
|
|
2082
|
+
return positionalSqlIsHidden(invocation.args, syntax) || invocation.args.some((_, index) => {
|
|
2083
|
+
const { script, sql, hidden } = sqlArgumentAt(invocation.args, index, syntax);
|
|
2084
|
+
return script || hidden || sql !== null && writesThroughSql(sql);
|
|
2085
|
+
});
|
|
2086
|
+
};
|
|
2087
|
+
var PULL_REQUEST_CLIS = /* @__PURE__ */ new Set(["gh", "glab"]);
|
|
2088
|
+
var PULL_REQUEST_NOUNS = /* @__PURE__ */ new Set(["pr", "mr"]);
|
|
2089
|
+
var MERGE_SUBCOMMAND = "merge";
|
|
2090
|
+
var PULL_REQUEST_MERGE_PATH = /(?:^|\/)pulls\/\d+\/merge$/;
|
|
2091
|
+
var mergesAPullRequest = ({ program, args }) => {
|
|
2092
|
+
if (program === void 0 || !PULL_REQUEST_CLIS.has(basenameOf(program.text))) return false;
|
|
2093
|
+
const words = args.filter((arg) => !arg.text.startsWith("-"));
|
|
2094
|
+
return words.some((word, index) => PULL_REQUEST_NOUNS.has(word.text) && words[index + 1]?.text === MERGE_SUBCOMMAND) || args.some((arg) => PULL_REQUEST_MERGE_PATH.test(arg.text));
|
|
2095
|
+
};
|
|
2096
|
+
var DEPLOY_AND_SCHEMA_COMMAND_PATTERNS = [
|
|
2097
|
+
"Bash(kubectl apply:*)",
|
|
2098
|
+
"Bash(kubectl replace:*)",
|
|
2099
|
+
"Bash(kubectl scale:*)",
|
|
2100
|
+
"Bash(kubectl rollout restart:*)",
|
|
2101
|
+
"Bash(kubectl rollout undo:*)",
|
|
2102
|
+
"Bash(helm install:*)",
|
|
2103
|
+
"Bash(helm upgrade:*)",
|
|
2104
|
+
"Bash(helm rollback:*)",
|
|
2105
|
+
"Bash(vercel promote:*)",
|
|
2106
|
+
"Bash(vercel rollback:*)",
|
|
2107
|
+
"Bash(railway up:*)",
|
|
2108
|
+
"Bash(prisma db push:*)",
|
|
2109
|
+
"Bash(prisma migrate reset:*)",
|
|
2110
|
+
"Bash(supabase db push:*)",
|
|
2111
|
+
"Bash(supabase db reset:*)",
|
|
2112
|
+
"Bash(drizzle-kit push:*)",
|
|
2113
|
+
"Bash(docker push:*)",
|
|
2114
|
+
"Bash(podman push:*)",
|
|
2115
|
+
"Bash(twine upload:*)",
|
|
2116
|
+
"Bash(npm publish:*)",
|
|
2117
|
+
"Bash(npm unpublish:*)",
|
|
2118
|
+
"Bash(cargo publish:*)",
|
|
2119
|
+
"Bash(gem push:*)",
|
|
2120
|
+
"Bash(poetry publish:*)",
|
|
2121
|
+
"Bash(uv publish:*)",
|
|
2122
|
+
"Bash(pnpm publish:*)",
|
|
2123
|
+
"Bash(yarn publish:*)",
|
|
2124
|
+
"Bash(yarn npm publish:*)",
|
|
2125
|
+
"Bash(bun publish:*)",
|
|
2126
|
+
"Bash(gh release create:*)",
|
|
2127
|
+
"Bash(pulumi up:*)",
|
|
2128
|
+
"Bash(pulumi destroy:*)",
|
|
2129
|
+
"Bash(cdk deploy:*)",
|
|
2130
|
+
"Bash(cdk destroy:*)",
|
|
2131
|
+
"Bash(tofu apply:*)",
|
|
2132
|
+
"Bash(tofu destroy:*)",
|
|
2133
|
+
"Bash(terraform apply:*)",
|
|
2134
|
+
"Bash(terraform destroy:*)",
|
|
2135
|
+
"Bash(firebase deploy:*)",
|
|
2136
|
+
"Bash(fly deploy:*)",
|
|
2137
|
+
"Bash(flyctl deploy:*)",
|
|
2138
|
+
"Bash(wrangler deploy:*)",
|
|
2139
|
+
"Bash(wrangler publish:*)",
|
|
2140
|
+
"Bash(serverless deploy:*)",
|
|
2141
|
+
"Bash(sls deploy:*)",
|
|
2142
|
+
"Bash(gcloud app deploy:*)",
|
|
2143
|
+
"Bash(gcloud run deploy:*)",
|
|
2144
|
+
"Bash(gcloud functions deploy:*)"
|
|
2145
|
+
];
|
|
2146
|
+
var SECRET_FILE_NAME = /^(?:\.env(?:\.(?!example$|sample$|template$)[\w.-]+)?|(?:secrets?|credentials)\.(?:json|ya?ml|env|toml|ini)|credentials)$/i;
|
|
2147
|
+
var REDIRECTS_INTO_A_SECRET_FILE = />>?\s*(?:[^\s;|&<>]*\/)?\.env(?:\.[\w-]+)?(?=$|[\s;|&)])/;
|
|
2148
|
+
var COPIES_ONTO_ITS_LAST_OPERAND = /* @__PURE__ */ new Set(["cp", "install", "ln", "rsync", "scp"]);
|
|
2149
|
+
var WRITES_EVERY_OPERAND = /* @__PURE__ */ new Set(["mv", "tee", "rm", "unlink", "shred", "truncate"]);
|
|
2150
|
+
var EDITS_IN_PLACE = /* @__PURE__ */ new Set(["sed", "gsed", "perl"]);
|
|
2151
|
+
var IN_PLACE_FLAG = /^(?:-[A-Za-z]*i|--in-place(?:=.*)?)$/;
|
|
2152
|
+
var namesASecretFile = (word) => word !== void 0 && SECRET_FILE_NAME.test(basenameOf(word.text));
|
|
2153
|
+
var NETWORK_SENDERS = /* @__PURE__ */ new Set([
|
|
2154
|
+
"curl",
|
|
2155
|
+
"wget",
|
|
2156
|
+
"scp",
|
|
2157
|
+
"sftp",
|
|
2158
|
+
"rsync",
|
|
2159
|
+
"nc",
|
|
2160
|
+
"ncat",
|
|
2161
|
+
"http",
|
|
2162
|
+
"https",
|
|
2163
|
+
"xh",
|
|
2164
|
+
"gh",
|
|
2165
|
+
"aws",
|
|
2166
|
+
"gsutil",
|
|
2167
|
+
"rclone"
|
|
2168
|
+
]);
|
|
2169
|
+
var FILE_REFERENCE_PREFIX = /^(?:[\w.-]+=)?@|^[\w.-]+:/;
|
|
2170
|
+
var namesASecretFileReference = (word) => SECRET_FILE_NAME.test(basenameOf(word.text.replace(FILE_REFERENCE_PREFIX, "")));
|
|
2171
|
+
var sendsASecretFile = ({ program, args }) => program !== void 0 && NETWORK_SENDERS.has(basenameOf(program.text)) && args.some(namesASecretFileReference);
|
|
2172
|
+
var writesASecretFile = ({ program, args }) => {
|
|
2173
|
+
if (program === void 0) return false;
|
|
2174
|
+
if (sendsASecretFile({ program, args })) return true;
|
|
2175
|
+
const name = basenameOf(program.text);
|
|
2176
|
+
const operands = args.filter((arg) => !FLAG.test(arg.text));
|
|
2177
|
+
if (COPIES_ONTO_ITS_LAST_OPERAND.has(name)) return namesASecretFile(operands.at(-1));
|
|
2178
|
+
if (WRITES_EVERY_OPERAND.has(name)) return operands.some(namesASecretFile);
|
|
2179
|
+
return EDITS_IN_PLACE.has(name) && args.some((arg) => IN_PLACE_FLAG.test(arg.text)) && operands.some(namesASecretFile);
|
|
2180
|
+
};
|
|
1827
2181
|
var segmentNeedsAPerson = (toolName, segment) => {
|
|
1828
2182
|
const invocation = invocationOf(segment);
|
|
1829
2183
|
const normalized = normalizedInvocation(invocation);
|
|
1830
|
-
return invocationHidesItsEffect(invocation) || subcommandDestroys(invocation) || classifyDecisionRisk(toolName, normalized) !== "standard" || isCoveredByPolicy(CAPABILITY_PATTERNS.destructive, toolName, normalized) || isCoveredByPolicy(CAPABILITY_PATTERNS.network, toolName, normalized) || isCoveredByPolicy(DATA_LOSS_COMMAND_PATTERNS, toolName, normalized);
|
|
2184
|
+
return invocationHidesItsEffect(invocation) || subcommandDestroys(invocation) || changesWhatIsDeployed(toolName, invocation, normalized) || sqlClientChangesData(invocation) || classifyDecisionRisk(toolName, normalized) !== "standard" || isCoveredByPolicy(CAPABILITY_PATTERNS.destructive, toolName, normalized) || isCoveredByPolicy(CAPABILITY_PATTERNS.network, toolName, normalized) || isCoveredByPolicy(DATA_LOSS_COMMAND_PATTERNS, toolName, normalized);
|
|
1831
2185
|
};
|
|
1832
2186
|
var commandNeedsAPerson = (toolName, command) => {
|
|
1833
2187
|
if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
|
|
1834
2188
|
const segments = readableCommandSegments(command);
|
|
1835
|
-
return segments === null || classifyDecisionRisk(toolName, command) !== "standard" || segments.some((segment) => segmentNeedsAPerson(toolName, segment));
|
|
2189
|
+
return segments === null || classifyDecisionRisk(toolName, command) !== "standard" || segments.some((segment) => segmentNeedsAPerson(toolName, segment)) || pipesIntoASqlClient(command);
|
|
2190
|
+
};
|
|
2191
|
+
var VISIBLE_DATA_LOSS_PATTERNS = [
|
|
2192
|
+
"Bash(git branch -D:*)",
|
|
2193
|
+
"Bash(git branch --delete:*)",
|
|
2194
|
+
"Bash(git stash drop:*)",
|
|
2195
|
+
"Bash(git stash clear:*)",
|
|
2196
|
+
"Bash(git filter-branch:*)",
|
|
2197
|
+
"Bash(git update-ref -d:*)",
|
|
2198
|
+
"Bash(git reflog expire:*)",
|
|
2199
|
+
"Bash(git prune:*)",
|
|
2200
|
+
"Bash(dd :*)",
|
|
2201
|
+
"Bash(shred:*)",
|
|
2202
|
+
"Bash(chmod -R:*)",
|
|
2203
|
+
"Bash(chown -R:*)",
|
|
2204
|
+
"Bash(dropdb:*)",
|
|
2205
|
+
"Bash(mkfs:*)",
|
|
2206
|
+
"Bash(rclone sync:*)",
|
|
2207
|
+
"Bash(rclone delete:*)",
|
|
2208
|
+
"Bash(rclone purge:*)",
|
|
2209
|
+
"Bash(diskutil eraseDisk:*)",
|
|
2210
|
+
"Bash(diskutil eraseVolume:*)",
|
|
2211
|
+
"Bash(diskutil zeroDisk:*)",
|
|
2212
|
+
"Bash(diskutil secureErase:*)",
|
|
2213
|
+
"Bash(diskutil partitionDisk:*)",
|
|
2214
|
+
"Bash(diskutil reformat:*)"
|
|
2215
|
+
];
|
|
2216
|
+
var NEW_BRANCH_FLAGS = /* @__PURE__ */ new Set(["-b", "--orphan"]);
|
|
2217
|
+
var CHECKOUT_PATH_FLAGS = /* @__PURE__ */ new Set(["-p", "--patch", "--ours", "--theirs", "--pathspec-from-file"]);
|
|
2218
|
+
var UNSTAGE_ONLY_FLAGS = /* @__PURE__ */ new Set(["--staged", "-S"]);
|
|
2219
|
+
var WORKTREE_FLAGS = /* @__PURE__ */ new Set(["--worktree", "-W"]);
|
|
2220
|
+
var DISCARDING_RESET_MODES = /* @__PURE__ */ new Set(["--hard", "--merge", "--keep"]);
|
|
2221
|
+
var FORCE_FLAGS = /* @__PURE__ */ new Set(["-f", "--force"]);
|
|
2222
|
+
var REFSPEC_THAT_FORCES_OR_DELETES = /^[+:]/;
|
|
2223
|
+
var LOOKS_LIKE_A_PATH = /^\.{1,2}$|\/$|[*?[]|(?:^|\/)[^/]+\.[A-Za-z][A-Za-z0-9]*$/;
|
|
2224
|
+
var GIT_WORK_DISCARDERS = /* @__PURE__ */ new Map([
|
|
2225
|
+
["checkout", (flags, operands) => !flags.some((flag) => NEW_BRANCH_FLAGS.has(flag)) && (flags.some((flag) => CHECKOUT_PATH_FLAGS.has(flag)) || operands.length > 1 || operands.some((operand) => LOOKS_LIKE_A_PATH.test(operand)))],
|
|
2226
|
+
["restore", (flags) => !flags.some((flag) => UNSTAGE_ONLY_FLAGS.has(flag)) || flags.some((flag) => WORKTREE_FLAGS.has(flag))],
|
|
2227
|
+
["reset", (flags) => flags.some((flag) => DISCARDING_RESET_MODES.has(flag))],
|
|
2228
|
+
["push", (_flags, operands) => operands.some((operand) => REFSPEC_THAT_FORCES_OR_DELETES.test(operand))],
|
|
2229
|
+
["worktree", (flags, operands) => operands[0] === "remove" && flags.some((flag) => FORCE_FLAGS.has(flag))]
|
|
2230
|
+
]);
|
|
2231
|
+
var gitDiscardsWork = ({ program, args }) => {
|
|
2232
|
+
if (program === void 0 || basenameOf(program.text) !== "git") return false;
|
|
2233
|
+
const [subcommand, ...rest] = argsAfterHarmlessGlobalFlags(args);
|
|
2234
|
+
const discards = GIT_WORK_DISCARDERS.get(subcommand?.text ?? "");
|
|
2235
|
+
const texts = rest.map((arg) => arg.text);
|
|
2236
|
+
return discards !== void 0 && discards(texts.filter((text) => FLAG.test(text)), texts.filter((text) => !FLAG.test(text)));
|
|
2237
|
+
};
|
|
2238
|
+
var PACKAGE_MANAGERS = /* @__PURE__ */ new Set([
|
|
2239
|
+
"npm",
|
|
2240
|
+
"pnpm",
|
|
2241
|
+
"yarn",
|
|
2242
|
+
"bun",
|
|
2243
|
+
"pip",
|
|
2244
|
+
"pip3",
|
|
2245
|
+
"cargo",
|
|
2246
|
+
"brew",
|
|
2247
|
+
"gem",
|
|
2248
|
+
"uv",
|
|
2249
|
+
"poetry"
|
|
2250
|
+
]);
|
|
2251
|
+
var GIT = "git";
|
|
2252
|
+
var removesAPackage = ({ program, args }) => program !== void 0 && PACKAGE_MANAGERS.has(basenameOf(program.text)) && subcommandWordDestroys(subcommandWordsOf(argsAfterHarmlessGlobalFlags(args))[0]?.text ?? "");
|
|
2253
|
+
var subcommandDestroysVisibly = (invocation) => subcommandDestroys(invocation) && basenameOf(invocation.program?.text ?? "") !== GIT && !removesAPackage(invocation);
|
|
2254
|
+
var SERVICE_MANAGER_READ_VERBS = /* @__PURE__ */ new Map([
|
|
2255
|
+
["systemctl", /* @__PURE__ */ new Set([
|
|
2256
|
+
"status",
|
|
2257
|
+
"show",
|
|
2258
|
+
"cat",
|
|
2259
|
+
"help",
|
|
2260
|
+
"list-units",
|
|
2261
|
+
"list-unit-files",
|
|
2262
|
+
"list-timers",
|
|
2263
|
+
"list-sockets",
|
|
2264
|
+
"list-dependencies",
|
|
2265
|
+
"list-jobs",
|
|
2266
|
+
"is-active",
|
|
2267
|
+
"is-enabled",
|
|
2268
|
+
"is-failed",
|
|
2269
|
+
"is-system-running",
|
|
2270
|
+
"get-default"
|
|
2271
|
+
])],
|
|
2272
|
+
["launchctl", /* @__PURE__ */ new Set([
|
|
2273
|
+
"list",
|
|
2274
|
+
"print",
|
|
2275
|
+
"print-disabled",
|
|
2276
|
+
"print-cache",
|
|
2277
|
+
"blame",
|
|
2278
|
+
"help",
|
|
2279
|
+
"version",
|
|
2280
|
+
"getenv",
|
|
2281
|
+
"managerpid",
|
|
2282
|
+
"managername",
|
|
2283
|
+
"manageruid",
|
|
2284
|
+
"hostinfo",
|
|
2285
|
+
"dumpstate"
|
|
2286
|
+
])]
|
|
2287
|
+
]);
|
|
2288
|
+
var changesAService = ({ program, args }) => {
|
|
2289
|
+
const readVerbs = program === void 0 ? void 0 : SERVICE_MANAGER_READ_VERBS.get(basenameOf(program.text));
|
|
2290
|
+
const verb = args.find((arg) => !FLAG.test(arg.text))?.text;
|
|
2291
|
+
return readVerbs !== void 0 && verb !== void 0 && !readVerbs.has(verb);
|
|
2292
|
+
};
|
|
2293
|
+
var CRONTAB = "crontab";
|
|
2294
|
+
var CRONTAB_USER_FLAG = "-u";
|
|
2295
|
+
var CRONTAB_REMOVE_FLAG = "-r";
|
|
2296
|
+
var replacesTheCrontab = ({ program, args }) => program !== void 0 && basenameOf(program.text) === CRONTAB && args.some((arg, index) => arg.text === CRONTAB_REMOVE_FLAG || arg.text === STANDARD_INPUT_ARGUMENT || !FLAG.test(arg.text) && args[index - 1]?.text !== CRONTAB_USER_FLAG);
|
|
2297
|
+
var FIND_DELETE = "-delete";
|
|
2298
|
+
var FIND_RUNNERS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"]);
|
|
2299
|
+
var FIND_RUNNER_END = /* @__PURE__ */ new Set([";", "+"]);
|
|
2300
|
+
var losesDataVisibly = (toolName, invocation, normalized, depth) => {
|
|
2301
|
+
const { program, args } = invocation;
|
|
2302
|
+
if (program === void 0) return false;
|
|
2303
|
+
const name = basenameOf(program.text);
|
|
2304
|
+
if (name === "find") {
|
|
2305
|
+
return args.some((arg) => arg.text === FIND_DELETE) || args.some((arg, index) => FIND_RUNNERS.has(arg.text) && invocationHasAVisibleConsequence(toolName, {
|
|
2306
|
+
program: args[index + 1],
|
|
2307
|
+
args: args.slice(index + 2, findRunnerEnd(args, index + 2))
|
|
2308
|
+
}, depth + 1));
|
|
2309
|
+
}
|
|
2310
|
+
const destructiveArguments = destructiveArgumentsOf(name, args);
|
|
2311
|
+
return destructiveArguments !== void 0 && args.some((arg) => namesDestructiveArgument(arg.text, destructiveArguments)) || namesDestructiveWordInAnyCase(name, args.map((arg) => arg.text)) || gitDiscardsWork(invocation) || changesAService(invocation) || replacesTheCrontab(invocation) || isCoveredByPolicy(VISIBLE_DATA_LOSS_PATTERNS, toolName, normalized);
|
|
2312
|
+
};
|
|
2313
|
+
var findRunnerEnd = (args, from) => {
|
|
2314
|
+
const end = args.findIndex((arg, index) => index >= from && FIND_RUNNER_END.has(arg.text));
|
|
2315
|
+
return end === -1 ? args.length : end;
|
|
2316
|
+
};
|
|
2317
|
+
var wrapperSyntax = (optionsWithAValue = [], operandsBeforeTheCommand = 0) => ({ optionsWithAValue: new Set(optionsWithAValue), operandsBeforeTheCommand });
|
|
2318
|
+
var ENVIRONMENT_ASSIGNMENTS = wrapperSyntax();
|
|
2319
|
+
var WRAPPER_SYNTAX = /* @__PURE__ */ new Map([
|
|
2320
|
+
["env", wrapperSyntax(["-u", "--unset", "-C", "--chdir", "-S", "--split-string", "-P"])],
|
|
2321
|
+
["xargs", wrapperSyntax([
|
|
2322
|
+
"-I",
|
|
2323
|
+
"-J",
|
|
2324
|
+
"-R",
|
|
2325
|
+
"-S",
|
|
2326
|
+
"-n",
|
|
2327
|
+
"-L",
|
|
2328
|
+
"-P",
|
|
2329
|
+
"-s",
|
|
2330
|
+
"-d",
|
|
2331
|
+
"-E",
|
|
2332
|
+
"-a",
|
|
2333
|
+
"--max-args",
|
|
2334
|
+
"--max-lines",
|
|
2335
|
+
"--max-procs",
|
|
2336
|
+
"--max-chars",
|
|
2337
|
+
"--delimiter",
|
|
2338
|
+
"--arg-file",
|
|
2339
|
+
"--eof",
|
|
2340
|
+
"--replace"
|
|
2341
|
+
])],
|
|
2342
|
+
["timeout", wrapperSyntax(["-s", "--signal", "-k", "--kill-after"], 1)],
|
|
2343
|
+
["nice", wrapperSyntax(["-n", "--adjustment"])],
|
|
2344
|
+
["ionice", wrapperSyntax(["-c", "--class", "-n", "--classdata"])],
|
|
2345
|
+
["stdbuf", wrapperSyntax(["-i", "-o", "-e", "--input", "--output", "--error"])],
|
|
2346
|
+
["flock", wrapperSyntax(["-w", "--wait", "--timeout", "-E", "--conflict-exit-code"], 1)],
|
|
2347
|
+
["caffeinate", wrapperSyntax(["-t", "-w"])],
|
|
2348
|
+
["sudo", wrapperSyntax([
|
|
2349
|
+
"-u",
|
|
2350
|
+
"--user",
|
|
2351
|
+
"-g",
|
|
2352
|
+
"--group",
|
|
2353
|
+
"-C",
|
|
2354
|
+
"--close-from",
|
|
2355
|
+
"-h",
|
|
2356
|
+
"--host",
|
|
2357
|
+
"-p",
|
|
2358
|
+
"--prompt",
|
|
2359
|
+
"-U",
|
|
2360
|
+
"--other-user",
|
|
2361
|
+
"-r",
|
|
2362
|
+
"--role",
|
|
2363
|
+
"-t",
|
|
2364
|
+
"--type",
|
|
2365
|
+
"-D",
|
|
2366
|
+
"--chdir"
|
|
2367
|
+
])],
|
|
2368
|
+
["doas", wrapperSyntax(["-u", "-C"])],
|
|
2369
|
+
["time", wrapperSyntax(["-f", "--format", "-o", "--output"])],
|
|
2370
|
+
["watch", wrapperSyntax(["-n", "--interval"])],
|
|
2371
|
+
["nohup", wrapperSyntax()],
|
|
2372
|
+
["command", wrapperSyntax()],
|
|
2373
|
+
["builtin", wrapperSyntax()],
|
|
2374
|
+
["exec", wrapperSyntax(["-a"])],
|
|
2375
|
+
["unbuffer", wrapperSyntax()],
|
|
2376
|
+
["npx", wrapperSyntax(["-p", "--package", "-c", "--call"])],
|
|
2377
|
+
["bunx", wrapperSyntax(["-p", "--package"])],
|
|
2378
|
+
["pnpx", wrapperSyntax()],
|
|
2379
|
+
["uvx", wrapperSyntax(["--from", "--with", "-p", "--python"])]
|
|
2380
|
+
]);
|
|
2381
|
+
var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
2382
|
+
var SCRIPT_FLAG_CLUSTER = /^-[A-Za-z]*c$/;
|
|
2383
|
+
var EVAL = "eval";
|
|
2384
|
+
var MAX_CONSEQUENCE_DEPTH = 4;
|
|
2385
|
+
var commandStartAfterWrapper = (words, syntax) => {
|
|
2386
|
+
let index = 0;
|
|
2387
|
+
while (index < words.length) {
|
|
2388
|
+
const text = words[index]?.text ?? "";
|
|
2389
|
+
if (text === END_OF_OPTIONS) return index + 1 + syntax.operandsBeforeTheCommand;
|
|
2390
|
+
if (!SHELL_ENV_ASSIGNMENT.test(text) && (!FLAG.test(text) || text === STANDARD_INPUT_ARGUMENT)) break;
|
|
2391
|
+
index += syntax.optionsWithAValue.has(text) ? 2 : 1;
|
|
2392
|
+
}
|
|
2393
|
+
return index + syntax.operandsBeforeTheCommand;
|
|
2394
|
+
};
|
|
2395
|
+
var wrappedInvocation = ({ program, args }) => {
|
|
2396
|
+
if (program === void 0) return null;
|
|
2397
|
+
const assigns = SHELL_ENV_ASSIGNMENT.test(program.text);
|
|
2398
|
+
const syntax = assigns ? ENVIRONMENT_ASSIGNMENTS : WRAPPER_SYNTAX.get(basenameOf(program.text));
|
|
2399
|
+
if (syntax === void 0) return null;
|
|
2400
|
+
const words = assigns ? [program, ...args] : args;
|
|
2401
|
+
const start = commandStartAfterWrapper(words, syntax);
|
|
2402
|
+
const command = words[start];
|
|
2403
|
+
return command === void 0 ? null : { program: command, args: words.slice(start + 1) };
|
|
2404
|
+
};
|
|
2405
|
+
var innermostInvocation = (invocation, depth = 0) => {
|
|
2406
|
+
const wrapped = depth < MAX_CONSEQUENCE_DEPTH ? wrappedInvocation(invocation) : null;
|
|
2407
|
+
return wrapped === null ? invocation : innermostInvocation(wrapped, depth + 1);
|
|
2408
|
+
};
|
|
2409
|
+
var shellScriptOf = ({ program, args }) => {
|
|
2410
|
+
if (program === void 0) return null;
|
|
2411
|
+
if (basenameOf(program.text) === EVAL) return args.some((arg) => arg.expands) ? null : args.map((arg) => arg.text).join(" ");
|
|
2412
|
+
if (!SHELLS.has(basenameOf(program.text))) return null;
|
|
2413
|
+
const flag = args.findIndex((arg) => SCRIPT_FLAG_CLUSTER.test(arg.text));
|
|
2414
|
+
const script = flag === -1 ? void 0 : args[flag + 1];
|
|
2415
|
+
return script === void 0 || script.expands ? null : script.text;
|
|
2416
|
+
};
|
|
2417
|
+
var FLAG = /^-/;
|
|
2418
|
+
var FLAG_WITH_ATTACHED_VALUE = /^-[^=]*=/;
|
|
2419
|
+
var withoutLeadingFlags = (args, flagsTakeAValue) => {
|
|
2420
|
+
let index = 0;
|
|
2421
|
+
while (index < args.length && FLAG.test(args[index]?.text ?? "")) {
|
|
2422
|
+
const takesNext = flagsTakeAValue && !FLAG_WITH_ATTACHED_VALUE.test(args[index]?.text ?? "") && !FLAG.test(args[index + 1]?.text ?? "-");
|
|
2423
|
+
index += takesNext ? 2 : 1;
|
|
2424
|
+
}
|
|
2425
|
+
return args.slice(index);
|
|
2426
|
+
};
|
|
2427
|
+
var readingsPastGlobalFlags = ({ program, args }) => {
|
|
2428
|
+
const meaningful = argsAfterHarmlessGlobalFlags(args);
|
|
2429
|
+
if (program === void 0 || !SUBCOMMAND_CLIS.has(basenameOf(program.text)) || !FLAG.test(meaningful[0]?.text ?? "")) return [];
|
|
2430
|
+
return [false, true].map((flagsTakeAValue) => ({ program, args: withoutLeadingFlags(meaningful, flagsTakeAValue) }));
|
|
2431
|
+
};
|
|
2432
|
+
var PREVIEW_FLAG = /^--(?:help|dry-?run(?:=(?:client|server|true))?)$/;
|
|
2433
|
+
var TASK_RUNNERS = /* @__PURE__ */ new Set(["make", "just", "rake", "task"]);
|
|
2434
|
+
var PUBLISH_SUBCOMMAND = "publish";
|
|
2435
|
+
var honoursPreviewFlags = ({ program, args }) => {
|
|
2436
|
+
const name = basenameOf(program?.text ?? "");
|
|
2437
|
+
if (TASK_RUNNERS.has(name)) return false;
|
|
2438
|
+
return !PACKAGE_MANAGERS.has(name) || subcommandWordsOf(argsAfterHarmlessGlobalFlags(args))[0]?.text === PUBLISH_SUBCOMMAND;
|
|
2439
|
+
};
|
|
2440
|
+
var onlyPreviews = (invocation) => invocation.program !== void 0 && honoursPreviewFlags(invocation) && invocation.args.some((arg) => !arg.expands && PREVIEW_FLAG.test(arg.text));
|
|
2441
|
+
var changesWhatIsDeployed = (toolName, invocation, normalized) => !onlyPreviews(invocation) && (changesProduction(invocation) || mergesAPullRequest(invocation) || isCoveredByPolicy(DEPLOY_AND_SCHEMA_COMMAND_PATTERNS, toolName, normalized));
|
|
2442
|
+
var invocationHasAVisibleConsequence = (toolName, invocation, depth) => {
|
|
2443
|
+
if (invocation.program === void 0 || depth > MAX_CONSEQUENCE_DEPTH) return false;
|
|
2444
|
+
const normalized = quotedInvocation(invocation);
|
|
2445
|
+
const wrapped = wrappedInvocation(invocation);
|
|
2446
|
+
const script = shellScriptOf(invocation);
|
|
2447
|
+
return classifyDecisionRisk(toolName, normalized) === "destructive" || changesWhatIsDeployed(toolName, invocation, normalized) || writesASecretFile(invocation) || losesDataVisibly(toolName, invocation, normalized, depth) || subcommandDestroysVisibly(invocation) || sqlClientChangesData(invocation) || wrapped !== null && invocationHasAVisibleConsequence(toolName, wrapped, depth + 1) || script !== null && commandHasAVisibleConsequenceAt(toolName, script, depth + 1) || readingsPastGlobalFlags(invocation).some((reading) => invocationHasAVisibleConsequence(toolName, reading, depth + 1));
|
|
2448
|
+
};
|
|
2449
|
+
var RUNS_FETCHED_CODE = /(?:\$\(|`|<\()\s*(?:curl|wget)\b/;
|
|
2450
|
+
var FETCHING_PROGRAMS = /* @__PURE__ */ new Set(["curl", "wget"]);
|
|
2451
|
+
var READS_ITS_SCRIPT_FROM_STANDARD_INPUT = /^-[A-Za-z]*s[A-Za-z]*$/;
|
|
2452
|
+
var interpreterNameOf = (program) => basenameOf(program.text).replace(/\d+(?:\.\d+)*$/, "");
|
|
2453
|
+
var runsScripts = (program) => SHELLS.has(basenameOf(program.text)) || INLINE_CODE_INTERPRETERS.has(interpreterNameOf(program));
|
|
2454
|
+
var runsWhatItReads = ({ program, args }) => program !== void 0 && runsScripts(program) && (args.length === 0 || args.every((arg) => arg.text.startsWith("-")) || args.some((arg) => arg.text === STANDARD_INPUT_ARGUMENT) || SHELLS.has(basenameOf(program.text)) && args.some((arg) => READS_ITS_SCRIPT_FROM_STANDARD_INPUT.test(arg.text)));
|
|
2455
|
+
var fetches = (invocation) => FETCHING_PROGRAMS.has(basenameOf(innermostInvocation(invocation).program?.text ?? ""));
|
|
2456
|
+
var URL_QUERY_OR_FRAGMENT = /[?#].*$/;
|
|
2457
|
+
var SOURCES_A_SCRIPT = /* @__PURE__ */ new Set(["source", "."]);
|
|
2458
|
+
var downloadedNamesOf = (invocations) => new Set(invocations.filter(fetches).flatMap((invocation) => innermostInvocation(invocation).args.filter((arg) => !FLAG.test(arg.text)).map((arg) => basenameOf(arg.text.replace(URL_QUERY_OR_FRAGMENT, "")))));
|
|
2459
|
+
var runsADownloadedScript = (invocations) => {
|
|
2460
|
+
const downloaded = downloadedNamesOf(invocations);
|
|
2461
|
+
return downloaded.size > 0 && invocations.map((invocation) => innermostInvocation(invocation)).some(({ program, args }) => {
|
|
2462
|
+
if (program === void 0) return false;
|
|
2463
|
+
const script = args.find((arg) => !FLAG.test(arg.text))?.text;
|
|
2464
|
+
return downloaded.has(basenameOf(program.text)) || (runsScripts(program) || SOURCES_A_SCRIPT.has(program.text)) && script !== void 0 && downloaded.has(basenameOf(script));
|
|
2465
|
+
});
|
|
2466
|
+
};
|
|
2467
|
+
var pipedSegmentsOf = (command) => (shellSegmentsReadingAPipe(commandForReading(command)) ?? []).map((segment) => innermostInvocation(invocationOf(withoutShellGrouping(segment))));
|
|
2468
|
+
var pipesIntoASqlClient = (command) => pipedSegmentsOf(command).some((invocation) => sqlClientSyntaxOf(invocation) !== void 0);
|
|
2469
|
+
var pipesFetchedCodeIntoAnInterpreter = (command, invocations) => invocations.some(fetches) && pipedSegmentsOf(command).some(runsWhatItReads);
|
|
2470
|
+
var SHELL_GROUPING_PREFIX = /^\s*(?:(?:if|then|elif|else|do|while|until|!)\s+|[({]\s*)+/;
|
|
2471
|
+
var GROUP_OPENER_BY_CLOSER = /* @__PURE__ */ new Map([[")", "("], ["}", "{"]]);
|
|
2472
|
+
var occurrencesOf = (text, character) => text.split(character).length - 1;
|
|
2473
|
+
var closesAGroupItDidNotOpen = (text) => {
|
|
2474
|
+
const last = text.at(-1) ?? "";
|
|
2475
|
+
const opener = GROUP_OPENER_BY_CLOSER.get(last);
|
|
2476
|
+
return opener !== void 0 && occurrencesOf(text, last) > occurrencesOf(text, opener);
|
|
2477
|
+
};
|
|
2478
|
+
var withoutShellGrouping = (segment) => {
|
|
2479
|
+
let text = segment.replace(SHELL_GROUPING_PREFIX, "").trimEnd();
|
|
2480
|
+
while (closesAGroupItDidNotOpen(text)) text = text.slice(0, -1).trimEnd();
|
|
2481
|
+
return text;
|
|
2482
|
+
};
|
|
2483
|
+
var HEREDOC_OPERATOR = /(?<!<)\d*<<(?!<)-?[ \t]*(?:'([A-Za-z_]\w*)'|"([A-Za-z_]\w*)"|\\?([A-Za-z_]\w*))/g;
|
|
2484
|
+
var HEREDOC_MARK = "\uE001";
|
|
2485
|
+
var withHeredocBodiesSetAside = (command) => {
|
|
2486
|
+
const lines = command.split("\n");
|
|
2487
|
+
const kept = [];
|
|
2488
|
+
const bodies = [];
|
|
2489
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2490
|
+
const line = lines[index] ?? "";
|
|
2491
|
+
const delimiters = [...line.matchAll(HEREDOC_OPERATOR)].map((match) => match[1] ?? match[2] ?? match[3] ?? "");
|
|
2492
|
+
kept.push(line.replace(HEREDOC_OPERATOR, ` ${HEREDOC_MARK} `));
|
|
2493
|
+
for (const delimiter of delimiters) {
|
|
2494
|
+
const body = [];
|
|
2495
|
+
for (index += 1; index < lines.length && !mightEndAHeredoc(lines[index] ?? "", delimiter); index += 1) {
|
|
2496
|
+
body.push(lines[index] ?? "");
|
|
2497
|
+
}
|
|
2498
|
+
bodies.push(body.join("\n"));
|
|
2499
|
+
const ending = lines[index];
|
|
2500
|
+
if (ending !== void 0 && !isHeredocTerminator(ending, delimiter)) kept.push(ending);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
return { text: kept.join("\n"), bodies };
|
|
2504
|
+
};
|
|
2505
|
+
var REDIRECTION_TARGET = String.raw`(?:'[^']*'|"[^"]*"|[^\s;|&()<>]+)`;
|
|
2506
|
+
var INPUT_REDIRECTION = new RegExp(String.raw`\d*(?:<<<|<(?![<(&>]))\s*${REDIRECTION_TARGET}`, "g");
|
|
2507
|
+
var OUTPUT_REDIRECTION = new RegExp(String.raw`(?:\d*|&)(?:>>?|>\||<>|>&|<&)\s*${REDIRECTION_TARGET}`, "g");
|
|
2508
|
+
var REDIRECTED_INPUT_MARK = "\uE000";
|
|
2509
|
+
var ROUGH_SEPARATORS = ["&&", "||", "$(", "\n", ";", "|", "&", "`", "(", ")", "{", "}"];
|
|
2510
|
+
var SEPARATORS_INSIDE_DOUBLE_QUOTES = /* @__PURE__ */ new Set(["$(", "`", ")"]);
|
|
2511
|
+
var PIPE2 = "|";
|
|
2512
|
+
var SUBSTITUTION_OPENERS = /* @__PURE__ */ new Set(["$(", "`"]);
|
|
2513
|
+
var QUOTE_CHARACTERS = /['"]/g;
|
|
2514
|
+
var roughPiecesOf = (text) => {
|
|
2515
|
+
const pieces = [];
|
|
2516
|
+
let current = "";
|
|
2517
|
+
let before = "";
|
|
2518
|
+
let quote = null;
|
|
2519
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
2520
|
+
const char = text.charAt(index);
|
|
2521
|
+
if (quote === "'") {
|
|
2522
|
+
current += char;
|
|
2523
|
+
if (char === "'") quote = null;
|
|
2524
|
+
continue;
|
|
2525
|
+
}
|
|
2526
|
+
if (char === "\\") {
|
|
2527
|
+
current += text.slice(index, index + 2);
|
|
2528
|
+
index += 1;
|
|
2529
|
+
continue;
|
|
2530
|
+
}
|
|
2531
|
+
const separator = ROUGH_SEPARATORS.find((candidate) => text.startsWith(candidate, index) && (quote === null || SEPARATORS_INSIDE_DOUBLE_QUOTES.has(candidate)));
|
|
2532
|
+
if (separator !== void 0) {
|
|
2533
|
+
pieces.push({ text: current, before });
|
|
2534
|
+
current = "";
|
|
2535
|
+
before = separator;
|
|
2536
|
+
index += separator.length - 1;
|
|
2537
|
+
continue;
|
|
2538
|
+
}
|
|
2539
|
+
if (char === '"' || char === "'") quote = quote === char ? null : quote ?? char;
|
|
2540
|
+
current += char;
|
|
2541
|
+
}
|
|
2542
|
+
return [...pieces, { text: current, before }];
|
|
2543
|
+
};
|
|
2544
|
+
var invocationOfRoughPiece = (text) => {
|
|
2545
|
+
const piece = withoutShellGrouping(text.replaceAll(REDIRECTED_INPUT_MARK, "").replaceAll(HEREDOC_MARK, ""));
|
|
2546
|
+
const parsed = invocationOf(piece);
|
|
2547
|
+
return parsed.program !== void 0 ? parsed : invocationOf(piece.replace(QUOTE_CHARACTERS, ""));
|
|
2548
|
+
};
|
|
2549
|
+
var roughSegmentsOf = (command) => {
|
|
2550
|
+
const { text, bodies } = withHeredocBodiesSetAside(commandForReading(command));
|
|
2551
|
+
const pieces = roughPiecesOf(text.replace(INPUT_REDIRECTION, ` ${REDIRECTED_INPUT_MARK} `).replace(OUTPUT_REDIRECTION, " "));
|
|
2552
|
+
let heredocsSeen = 0;
|
|
2553
|
+
return pieces.map((piece, index) => {
|
|
2554
|
+
const heredocCount = occurrencesOf(piece.text, HEREDOC_MARK);
|
|
2555
|
+
const heredocs = bodies.slice(heredocsSeen, heredocsSeen + heredocCount);
|
|
2556
|
+
heredocsSeen += heredocCount;
|
|
2557
|
+
return {
|
|
2558
|
+
invocation: invocationOfRoughPiece(piece.text),
|
|
2559
|
+
readsInput: piece.text.includes(REDIRECTED_INPUT_MARK) || heredocCount > 0 || piece.before === PIPE2,
|
|
2560
|
+
readsAPipe: piece.before === PIPE2,
|
|
2561
|
+
feedsASubstitution: SUBSTITUTION_OPENERS.has(pieces[index + 1]?.before ?? ""),
|
|
2562
|
+
heredocs
|
|
2563
|
+
};
|
|
2564
|
+
});
|
|
2565
|
+
};
|
|
2566
|
+
var roughSegmentHasAVisibleConsequence = (toolName, segment, fetchesCode, depth) => {
|
|
2567
|
+
const innermost = innermostInvocation(segment.invocation);
|
|
2568
|
+
const runsAHeredocScript = innermost.program !== void 0 && SHELLS.has(basenameOf(innermost.program.text));
|
|
2569
|
+
return invocationHasAVisibleConsequence(toolName, segment.invocation, depth) || sqlClientSyntaxOf(innermost) !== void 0 && (segment.readsInput || segment.feedsASubstitution) || segment.readsAPipe && fetchesCode && runsWhatItReads(innermost) || runsAHeredocScript && segment.heredocs.some((body) => commandHasAVisibleConsequenceAt(toolName, body, depth + 1));
|
|
2570
|
+
};
|
|
2571
|
+
var roughReadingHasAVisibleConsequence = (toolName, command, depth) => {
|
|
2572
|
+
const segments = roughSegmentsOf(command);
|
|
2573
|
+
const invocations = segments.map((segment) => segment.invocation);
|
|
2574
|
+
const fetchesCode = invocations.some(fetches);
|
|
2575
|
+
return segments.some((segment) => roughSegmentHasAVisibleConsequence(toolName, segment, fetchesCode, depth)) || runsADownloadedScript(invocations);
|
|
2576
|
+
};
|
|
2577
|
+
var commandHasAVisibleConsequenceAt = (toolName, command, depth) => {
|
|
2578
|
+
if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
|
|
2579
|
+
const readable = withHeredocSubstitutionsAsQuotedText(command);
|
|
2580
|
+
if (classifyDecisionRisk(toolName, readable) === "destructive" || RUNS_FETCHED_CODE.test(readable) || REDIRECTS_INTO_A_SECRET_FILE.test(readable)) return true;
|
|
2581
|
+
const segments = readableCommandSegments(command);
|
|
2582
|
+
if (segments === null) return roughReadingHasAVisibleConsequence(toolName, command, depth);
|
|
2583
|
+
const invocations = segments.map((segment) => invocationOf(withoutShellGrouping(segment)));
|
|
2584
|
+
return invocations.some((invocation) => invocationHasAVisibleConsequence(toolName, invocation, depth)) || pipesIntoASqlClient(command) || pipesFetchedCodeIntoAnInterpreter(command, invocations) || runsADownloadedScript(invocations);
|
|
1836
2585
|
};
|
|
2586
|
+
var commandHasAVisibleConsequence = (toolName, command) => commandHasAVisibleConsequenceAt(toolName, command, 0);
|
|
1837
2587
|
var withoutSessionToolGrants = (config) => ({
|
|
1838
2588
|
...config,
|
|
1839
2589
|
policies: config.policies.filter((policy) => !isSessionToolGrant(policy))
|
|
1840
2590
|
});
|
|
1841
2591
|
var SESSION_TOOL_GRANT_TOOLS = /* @__PURE__ */ new Set(["Bash"]);
|
|
2592
|
+
var isStandingShellAllow = (policy) => !policy.origin && !isSessionToolGrant(policy) && SESSION_TOOL_GRANT_TOOLS.has(policy.tool) && isAutoApprove(policy);
|
|
1842
2593
|
var MCP_TOOL_PREFIX = "mcp__";
|
|
1843
2594
|
var parseMcpToolName = (toolName) => {
|
|
1844
2595
|
if (!toolName.startsWith(MCP_TOOL_PREFIX)) return void 0;
|
|
@@ -2460,6 +3211,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
|
|
|
2460
3211
|
};
|
|
2461
3212
|
const governedBySpecificRule = isSpecificArgumentRank(match?.rank);
|
|
2462
3213
|
const governedBySessionToolGrant = match?.rank === "tool" && isSessionToolGrant(match.policy);
|
|
3214
|
+
const governedByStandingShellAllow = match?.rank === "tool" && isStandingShellAllow(match.policy);
|
|
2463
3215
|
const governingMode = base.mode;
|
|
2464
3216
|
const approvedByReadOnlyFloor = !isAutoDeny(base) && (shellReadOnlyFloorApplies(toolName, arg, match?.rank) || coordinationFloorApplies(toolName, match?.rank));
|
|
2465
3217
|
if (governedBySessionToolGrant && !approvedByReadOnlyFloor && typeof arg === "string" && commandNeedsAPerson(toolName, arg)) {
|
|
@@ -2469,7 +3221,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
|
|
|
2469
3221
|
base = autoApprove(base.tool);
|
|
2470
3222
|
}
|
|
2471
3223
|
const wouldAutoApprove = isAutoApprove(base);
|
|
2472
|
-
if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && classifyDecisionRisk(toolName, arg) === "destructive") {
|
|
3224
|
+
if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && (classifyDecisionRisk(toolName, arg) === "destructive" || governedByStandingShellAllow && commandHasAVisibleConsequence(toolName, arg))) {
|
|
2473
3225
|
base = {
|
|
2474
3226
|
tool: base.tool,
|
|
2475
3227
|
timeoutSeconds: config.defaultTimeoutSeconds,
|