@pushary/agent-hooks 1.9.23 → 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 +15 -0
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CHANGELOG.md +4 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +4 -2
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +14 -1
- package/dist/bin/pushary-bell-hook.js +25 -25
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +27 -26
- package/dist/bin/pushary-clean.js +41 -93
- 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 +16 -16
- package/dist/bin/pushary-logout.js +77 -26
- 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 +41 -38
- 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-GN6BJXC7.js → chunk-2CUE3XXS.js} +1 -1
- package/dist/{chunk-OH6OGEOY.js → chunk-2J2WFX7X.js} +4 -4
- package/dist/{chunk-QIODXPTP.js → chunk-2VQZC6DW.js} +4 -4
- package/dist/{chunk-SWQLZQE3.js → chunk-2XHBYMVN.js} +14 -13
- package/dist/{chunk-HXZG5WHU.js → chunk-3UYMOWX6.js} +2 -2
- package/dist/{chunk-R5C3QCVT.js → chunk-4KZP4YMV.js} +1 -1
- package/dist/{chunk-73HGV4R6.js → chunk-5ETE7F45.js} +2 -2
- package/dist/{chunk-OQPEHKH2.js → chunk-5Q2QJ2AL.js} +1 -1
- package/dist/{chunk-K2EYK5RL.js → chunk-5WTEKIAS.js} +4 -4
- package/dist/{chunk-AV5VX7OK.js → chunk-7UZD2F4Y.js} +8 -8
- package/dist/{chunk-E2LGKG75.js → chunk-7V6PJFDF.js} +3 -3
- package/dist/{chunk-KJA6IGOZ.js → chunk-A3QQWXIE.js} +1 -1
- package/dist/{chunk-GYP6DLA4.js → chunk-AXB4ABUD.js} +8 -8
- package/dist/{chunk-I46QTKYI.js → chunk-BGJDOHNF.js} +5 -5
- package/dist/{chunk-FBV2KS3Z.js → chunk-BHRMHDOO.js} +2 -2
- package/dist/{chunk-RN5NRRHX.js → chunk-EJJNZYDS.js} +2 -2
- package/dist/{chunk-6HT2TUZD.js → chunk-EJMXZH5E.js} +2 -2
- package/dist/{chunk-N4DMADJM.js → chunk-FCXYDR62.js} +1 -1
- package/dist/{chunk-ZGYO6OQE.js → chunk-FJKV64M7.js} +5 -5
- package/dist/{chunk-QFNY4KUQ.js → chunk-FLC4TVXH.js} +1 -1
- package/dist/{chunk-NHBPW6C4.js → chunk-G446AVR5.js} +2 -2
- package/dist/{chunk-AQ3TSQA2.js → chunk-GC5HJE3T.js} +21 -8
- package/dist/{chunk-CJTQNUUT.js → chunk-HEJMZVJI.js} +2 -2
- package/dist/{chunk-OH647UT3.js → chunk-KGSN2SOL.js} +793 -18
- package/dist/{chunk-7RGEJOQS.js → chunk-KM7AEGKF.js} +3 -3
- package/dist/{chunk-FIYCICHC.js → chunk-KP4BIZLP.js} +2 -2
- package/dist/{chunk-YXHVMZPR.js → chunk-KZLOVLA6.js} +4 -4
- package/dist/{chunk-AXSHPFU3.js → chunk-LDNKGG5G.js} +3 -3
- package/dist/{chunk-DKBPNHAI.js → chunk-N3ACK3YA.js} +4 -4
- package/dist/{chunk-4GKQDFH6.js → chunk-NFH2H7PD.js} +5 -5
- package/dist/{chunk-BY5EKA6W.js → chunk-OFHSTCXK.js} +3 -3
- package/dist/{chunk-LEQRIJ4H.js → chunk-OOKIWOMK.js} +3 -3
- package/dist/{chunk-7J2AX5T6.js → chunk-OWOSNGEQ.js} +2 -2
- package/dist/{chunk-SSWK2WYX.js → chunk-PAMW5M53.js} +5 -5
- package/dist/{chunk-JNKHCGHR.js → chunk-PM2WJSUH.js} +1 -1
- package/dist/{chunk-JCEUDLGW.js → chunk-PPGLIWOR.js} +1 -1
- package/dist/{chunk-PFB7P4F3.js → chunk-PSNF27BC.js} +6 -6
- package/dist/{chunk-2AUKCSLQ.js → chunk-Q37XFSPO.js} +4 -4
- package/dist/{chunk-S2R2U5W5.js → chunk-RSENVGBQ.js} +4 -4
- package/dist/{chunk-M7DCI44L.js → chunk-SGGR5MN2.js} +1 -1
- package/dist/{chunk-NSCETODW.js → chunk-TLUSUDRU.js} +1 -1
- package/dist/chunk-UJB6HHP4.js +166 -0
- package/dist/{chunk-YZPFJCFE.js → chunk-UK7OY3EJ.js} +1 -1
- package/dist/{chunk-ISAKLEKE.js → chunk-UUOC7A7M.js} +7 -7
- package/dist/{chunk-Z4UDB7EC.js → chunk-VEGS4NYF.js} +3 -3
- package/dist/{chunk-USUI4P63.js → chunk-VFF7ZBIS.js} +2 -2
- package/dist/{chunk-RHIKMNWJ.js → chunk-VN5Q4KOD.js} +2 -2
- package/dist/{chunk-6GHZAS7S.js → chunk-WEKXYS2F.js} +4 -4
- package/dist/{chunk-SLBO7PHS.js → chunk-WKEP4JAW.js} +2 -2
- package/dist/{chunk-M6R3SZZS.js → chunk-YD3DIFIU.js} +1 -1
- package/dist/{chunk-EDN4F6GX.js → chunk-ZLGCNHJG.js} +1 -1
- package/dist/{chunk-A5U62Q6K.js → chunk-ZLY7BHN4.js} +5 -5
- package/dist/{chunk-AFP3HZVK.js → chunk-ZNT4ZZXJ.js} +1 -1
- package/dist/{chunk-XBETSM5T.js → chunk-ZV3262AA.js} +3 -3
- package/dist/{global-upgrade-SKY7A3SI.js → global-upgrade-4M4TJKQR.js} +6 -6
- package/dist/{paths-XUUEK7OU.js → paths-6JY6J56N.js} +2 -2
- package/dist/{reapply-WQDP26EA.js → reapply-LFEQ5GF6.js} +20 -20
- package/dist/src/index.js +19 -19
- package/dist/{upgrade-lock-AYW3TB7X.js → upgrade-lock-CZRCHYVU.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-OYIN77HO.js +0 -72
|
@@ -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;
|
|
@@ -913,6 +1004,8 @@ var approvalModeWithoutLocalPrompt = (mode) => {
|
|
|
913
1004
|
return mode;
|
|
914
1005
|
}
|
|
915
1006
|
};
|
|
1007
|
+
var TIMEOUT_ACTION_THAT_REFUSES = "deny";
|
|
1008
|
+
var timeoutActionWithoutLocalPrompt = ({ mode, timeoutAction }) => mode !== "push_only" && timeoutAction === "approve" ? TIMEOUT_ACTION_THAT_REFUSES : timeoutAction;
|
|
916
1009
|
var MAX_AGENT_QUESTIONS = 4;
|
|
917
1010
|
var parseAgentQuestion = (raw) => {
|
|
918
1011
|
const q = raw;
|
|
@@ -1181,11 +1274,12 @@ var PATTERN_INTENT = {
|
|
|
1181
1274
|
grant: "grant",
|
|
1182
1275
|
restrict: "restrict"
|
|
1183
1276
|
};
|
|
1277
|
+
var segmentsForMatching = (command) => shellCommandSegments(withHeredocSubstitutionsAsQuotedText(command));
|
|
1184
1278
|
var everySegmentMatches = (command, matches) => {
|
|
1185
|
-
const segments =
|
|
1279
|
+
const segments = segmentsForMatching(command);
|
|
1186
1280
|
return segments !== null && segments.every(matches);
|
|
1187
1281
|
};
|
|
1188
|
-
var anySegmentMatches = (command, matches) => (
|
|
1282
|
+
var anySegmentMatches = (command, matches) => (segmentsForMatching(command) ?? [command]).some(matches);
|
|
1189
1283
|
var shellSegmentsMatch = (command, intent, matches) => intent === PATTERN_INTENT.restrict ? anySegmentMatches(command, matches) : everySegmentMatches(command, matches);
|
|
1190
1284
|
var rankWholeOrSegments = (toolName, arg, intent, matches) => {
|
|
1191
1285
|
const matched = toolName in POLICY_COMMAND_KEYS ? shellSegmentsMatch(arg, intent, matches) : matches(arg);
|
|
@@ -1644,7 +1738,9 @@ var INLINE_CODE_INTERPRETERS = /* @__PURE__ */ new Set([
|
|
|
1644
1738
|
"lua",
|
|
1645
1739
|
"osascript",
|
|
1646
1740
|
"pwsh",
|
|
1647
|
-
"powershell"
|
|
1741
|
+
"powershell",
|
|
1742
|
+
"mongosh",
|
|
1743
|
+
"mongo"
|
|
1648
1744
|
]);
|
|
1649
1745
|
var INLINE_CODE_LONG_FLAG = /^-{1,2}(?:eval|print|require|import|command|encodedcommand|enc|ec)(?:=|$)/i;
|
|
1650
1746
|
var INLINE_CODE_SHORT_FLAG_LETTERS = /* @__PURE__ */ new Set(["c", "e", "E", "p", "r"]);
|
|
@@ -1667,7 +1763,15 @@ var DESTRUCTIVE_ARGUMENTS_BY_COMMAND = /* @__PURE__ */ new Map([
|
|
|
1667
1763
|
])],
|
|
1668
1764
|
["git checkout", /* @__PURE__ */ new Set(["-f", "--force", "--", "-B"])],
|
|
1669
1765
|
["git switch", /* @__PURE__ */ new Set(["-f", "--force", "--discard-changes", "-C", "--force-create"])],
|
|
1670
|
-
["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"])]
|
|
1671
1775
|
]);
|
|
1672
1776
|
var DESTRUCTIVE_WORDS_IN_ANY_CASE_BY_COMMAND = /* @__PURE__ */ new Map([
|
|
1673
1777
|
["redis-cli", /* @__PURE__ */ new Set(["flushall", "flushdb"])]
|
|
@@ -1708,7 +1812,8 @@ var SUBCOMMAND_CLIS = /* @__PURE__ */ new Set([
|
|
|
1708
1812
|
"heroku",
|
|
1709
1813
|
"supabase",
|
|
1710
1814
|
"wrangler",
|
|
1711
|
-
"railway"
|
|
1815
|
+
"railway",
|
|
1816
|
+
"bq"
|
|
1712
1817
|
]);
|
|
1713
1818
|
var DESTRUCTIVE_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
1714
1819
|
"delete",
|
|
@@ -1810,33 +1915,681 @@ var argumentsHideTheEffect = (name, args) => {
|
|
|
1810
1915
|
return (meaningful[0]?.text.startsWith("-") ?? false) || subcommandWordsOf(meaningful).some((word) => word.expands || RUNNER_SUBCOMMANDS.has(word.text));
|
|
1811
1916
|
};
|
|
1812
1917
|
var invocationHidesItsEffect = ({ program, args }) => program !== void 0 && ([program, ...args].some((word) => word.comment) || programHidesItsEffect(program) || argumentsHideTheEffect(basenameOf(program.text), args));
|
|
1813
|
-
var
|
|
1814
|
-
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) => {
|
|
1815
1921
|
if (program === void 0) return "";
|
|
1816
1922
|
const name = basenameOf(program.text);
|
|
1817
1923
|
const kept = SUBCOMMAND_CLIS.has(name) ? argsAfterHarmlessGlobalFlags(args) : args;
|
|
1818
|
-
return [name, ...kept.map(
|
|
1819
|
-
};
|
|
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));
|
|
1820
1932
|
var normalizedCommand = (command) => {
|
|
1821
1933
|
const segments = readableCommandSegments(command);
|
|
1822
1934
|
return segments === null ? null : segments.map((segment) => normalizedInvocation(invocationOf(segment))).join("; ");
|
|
1823
1935
|
};
|
|
1824
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
|
+
};
|
|
1825
2181
|
var segmentNeedsAPerson = (toolName, segment) => {
|
|
1826
2182
|
const invocation = invocationOf(segment);
|
|
1827
2183
|
const normalized = normalizedInvocation(invocation);
|
|
1828
|
-
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);
|
|
1829
2185
|
};
|
|
1830
2186
|
var commandNeedsAPerson = (toolName, command) => {
|
|
1831
2187
|
if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
|
|
1832
2188
|
const segments = readableCommandSegments(command);
|
|
1833
|
-
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);
|
|
1834
2585
|
};
|
|
2586
|
+
var commandHasAVisibleConsequence = (toolName, command) => commandHasAVisibleConsequenceAt(toolName, command, 0);
|
|
1835
2587
|
var withoutSessionToolGrants = (config) => ({
|
|
1836
2588
|
...config,
|
|
1837
2589
|
policies: config.policies.filter((policy) => !isSessionToolGrant(policy))
|
|
1838
2590
|
});
|
|
1839
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);
|
|
1840
2593
|
var MCP_TOOL_PREFIX = "mcp__";
|
|
1841
2594
|
var parseMcpToolName = (toolName) => {
|
|
1842
2595
|
if (!toolName.startsWith(MCP_TOOL_PREFIX)) return void 0;
|
|
@@ -1908,7 +2661,27 @@ var mcpToolNameNeedsAPerson = (toolName) => {
|
|
|
1908
2661
|
const phrase = words.join(" ");
|
|
1909
2662
|
return words.some(mcpToolWordNeedsAPerson) || classifyDecisionRisk(phrase, phrase) !== "standard";
|
|
1910
2663
|
};
|
|
1911
|
-
var
|
|
2664
|
+
var MCP_TOOL_VERBS_THAT_ONLY_READ = /* @__PURE__ */ new Set([
|
|
2665
|
+
"get",
|
|
2666
|
+
"list",
|
|
2667
|
+
"search",
|
|
2668
|
+
"read",
|
|
2669
|
+
"fetch",
|
|
2670
|
+
"find",
|
|
2671
|
+
"view",
|
|
2672
|
+
"show",
|
|
2673
|
+
"describe",
|
|
2674
|
+
"lookup",
|
|
2675
|
+
"retrieve",
|
|
2676
|
+
"inspect",
|
|
2677
|
+
"browse",
|
|
2678
|
+
"count"
|
|
2679
|
+
]);
|
|
2680
|
+
var mcpToolLeadsWithAReadVerb = (toolName) => {
|
|
2681
|
+
const tool = parseMcpToolName(toolName)?.tool;
|
|
2682
|
+
return tool !== void 0 && MCP_TOOL_VERBS_THAT_ONLY_READ.has(wordsOfMcpToolName(tool)[0] ?? "");
|
|
2683
|
+
};
|
|
2684
|
+
var allowsSessionToolGrant = (toolName) => SESSION_TOOL_GRANT_TOOLS.has(toolName) || mcpToolLeadsWithAReadVerb(toolName) && !isPusharyOwnTool(toolName.toLowerCase()) && !mcpToolNameNeedsAPerson(toolName);
|
|
1912
2685
|
var isSessionToolGrant = (policy) => Boolean(policy.sessionId) && allowsSessionToolGrant(policy.tool) && isAutoApprove(policy);
|
|
1913
2686
|
var API_KEY_PREFIX_SHAPE = "pk_[a-f0-9]+";
|
|
1914
2687
|
var API_KEY_PREFIX_PATTERN = new RegExp(`^${API_KEY_PREFIX_SHAPE}$`);
|
|
@@ -2438,6 +3211,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
|
|
|
2438
3211
|
};
|
|
2439
3212
|
const governedBySpecificRule = isSpecificArgumentRank(match?.rank);
|
|
2440
3213
|
const governedBySessionToolGrant = match?.rank === "tool" && isSessionToolGrant(match.policy);
|
|
3214
|
+
const governedByStandingShellAllow = match?.rank === "tool" && isStandingShellAllow(match.policy);
|
|
2441
3215
|
const governingMode = base.mode;
|
|
2442
3216
|
const approvedByReadOnlyFloor = !isAutoDeny(base) && (shellReadOnlyFloorApplies(toolName, arg, match?.rank) || coordinationFloorApplies(toolName, match?.rank));
|
|
2443
3217
|
if (governedBySessionToolGrant && !approvedByReadOnlyFloor && typeof arg === "string" && commandNeedsAPerson(toolName, arg)) {
|
|
@@ -2447,7 +3221,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
|
|
|
2447
3221
|
base = autoApprove(base.tool);
|
|
2448
3222
|
}
|
|
2449
3223
|
const wouldAutoApprove = isAutoApprove(base);
|
|
2450
|
-
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))) {
|
|
2451
3225
|
base = {
|
|
2452
3226
|
tool: base.tool,
|
|
2453
3227
|
timeoutSeconds: config.defaultTimeoutSeconds,
|
|
@@ -3200,6 +3974,7 @@ export {
|
|
|
3200
3974
|
normalizeKeyScope,
|
|
3201
3975
|
isApprovalMode,
|
|
3202
3976
|
approvalModeWithoutLocalPrompt,
|
|
3977
|
+
timeoutActionWithoutLocalPrompt,
|
|
3203
3978
|
parseAgentQuestion,
|
|
3204
3979
|
parseAgentQuestions,
|
|
3205
3980
|
parseAgentQuestionAnswers,
|