fullcourtdefense-cli 1.34.10 → 1.34.11

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.
@@ -6645,13 +6645,55 @@ function extractUrl(args, _argsText) {
6645
6645
  const direct = args.url || args.uri || args.endpoint || args.webhookUrl || args.callbackUrl || args.href || args.link;
6646
6646
  if (typeof direct === "string" && direct.trim()) return direct.trim();
6647
6647
  for (const field of URL_ACTION_FIELDS) {
6648
- const value = args[field];
6649
- if (typeof value !== "string") continue;
6648
+ const raw = args[field];
6649
+ if (typeof raw !== "string") continue;
6650
+ const value = ["command", "cmd", "script", "input"].includes(field) ? shellUrlActionText(raw) : raw;
6650
6651
  const found = value.match(/https?:\/\/[^\s"'<>]+/i)?.[0] || value.match(/\b(?:s3|gs|ftp|sftp|smb):\/\/[^\s"'<>]+/i)?.[0];
6651
6652
  if (found) return found;
6652
6653
  }
6653
6654
  return "";
6654
6655
  }
6656
+ function shellUrlActionText(command) {
6657
+ const segments = splitShellSegments(command);
6658
+ const filtered = segments.map((segment) => {
6659
+ const literal2 = /^(?:@'\r?\n[\s\S]*?\r?\n'@|@"\r?\n[^$`]*?\r?\n"@|'(?:[^']|'')*'|"[^"$`]*")/;
6660
+ const piped = segment.match(literal2);
6661
+ let tail = piped ? segment.slice(piped[0].length).replace(/^\s*\|\s*/, "") : "";
6662
+ if (!piped || !/^\s*\|/.test(segment.slice(piped[0].length))) {
6663
+ const argument = segment.match(/^(Set-Content|Add-Content|Out-File)\s+([\s\S]*?)\s+-(Value|InputObject)\s+([\s\S]+)$/i);
6664
+ const value = argument?.[4].match(literal2);
6665
+ if (!argument || !value) return segment;
6666
+ tail = `${argument[1]} ${argument[2]} ${argument[4].slice(value[0].length)}`;
6667
+ }
6668
+ const writer = tail.match(/^(?:Set-Content|Add-Content|Out-File)\s+([\s\S]+)$/i);
6669
+ if (!writer) return segment;
6670
+ const tokens = writer[1].match(/'[^']*'|"[^"$`]*"|[^\s'"$`|;&(){}]+/g) || [];
6671
+ if (tokens.join(" ").replace(/\s/g, "") !== writer[1].replace(/\s/g, "")) return segment;
6672
+ let target = "";
6673
+ for (let i2 = 0; i2 < tokens.length; i2++) {
6674
+ const token = tokens[i2];
6675
+ if (/^-(?:LiteralPath|Path|FilePath)$/i.test(token)) {
6676
+ if (target || !tokens[i2 + 1]) return segment;
6677
+ target = tokens[++i2];
6678
+ } else if (/^-Encoding$/i.test(token)) {
6679
+ if (!/^(?:utf8|utf8BOM|utf8NoBOM|unicode|ascii|default|oem|bigendianunicode|utf32)$/i.test(tokens[++i2] || "")) return segment;
6680
+ } else if (/^-(?:Force|NoNewline|Append)$/i.test(token)) {
6681
+ continue;
6682
+ } else if (!target && !token.startsWith("-")) target = token;
6683
+ else return segment;
6684
+ }
6685
+ target = target.replace(/^['"]|['"]$/g, "");
6686
+ if (!target || /^[\\/]{2}|[\r\n*?\[\]<>]/.test(target) || /:/.test(target.replace(/^[A-Za-z]:[\\/]/, ""))) return segment;
6687
+ return tail;
6688
+ });
6689
+ const onlyStoredData = segments.every((segment, index) => {
6690
+ if (filtered[index] !== segment) return true;
6691
+ const words = maskQuotedSpans(segment);
6692
+ if (!/[$`|;&(){}<>]/.test(segment) && /^(?:(?:cd|pushd|set-location)\s+[^\r\n]+|(?:pwd|popd|get-location)|git\s+status(?:\s+[^\r\n]+)?)$/i.test(segment)) return true;
6693
+ return !/[$`]/.test(segment) && /^\(Get-Content\s+(?:[A-Za-z]:[\\/])?[\w./\\-]+\s+-Raw\)\.Replace\("",\s*""\)\s*\|\s*Set-Content\s+(?:[A-Za-z]:[\\/])?[\w./\\-]+\s+-Encoding\s+utf8\s*$/i.test(words);
6694
+ });
6695
+ return onlyStoredData && filtered.some((value, index) => value !== segments[index]) ? filtered.join("\n") : command;
6696
+ }
6655
6697
  function splitShellSegments(command, alsoOnPipe = false) {
6656
6698
  const out = [];
6657
6699
  let cur = "";
@@ -6941,7 +6983,7 @@ function classifyShellWords(masked, raw, lead) {
6941
6983
  if (lead === "gh" && /\bgh\s+(?:pr|issue|repo|run|release|workflow)\s+(?:view|list|status|checks|diff|download)\b/.test(value)) return "read";
6942
6984
  if (lead === "gh" && /\bgh\s+auth\s+status\b/.test(value) && !/--show-token|\s-t\b/.test(value)) return "read";
6943
6985
  if (/^(?:gcloud\s+run\s+services\s+(?:describe|logs\s+read)|kubectl\s+(?:get|describe|logs|top|config\s+(?:current-context|get-contexts|view(?!\s+--raw)))|docker\s+(?:ps|images|logs|inspect|version|info)|terraform\s+(?:plan|validate|fmt|show|state\s+list|output)|aws\s+(?:s3\s+ls|sts\s+get-caller-identity)|az\s+account\s+show|npm\s+(?:ls|list|view|outdated|--version)|node\s+--version|python3?\s+--version)\b/.test(value)) return "read";
6944
- if (/\b(rm\s+-[a-z]*r[a-z]*f|rm\s+-[a-z]*f[a-z]*r|del\s+\/[fqs]|remove-item\b.*\b-recurse\b|rmdir\s+\/s)\b/.test(value)) {
6986
+ if (/\b(rm\s+-[a-z]*r[a-z]*f|rm\s+-[a-z]*f[a-z]*r|del\s+\/[fqs]|remove-item\b.*\s-recurse\b|rmdir\s+\/s)\b/.test(value)) {
6945
6987
  return isWorkspaceScopedRemoval(segment) ? "write" : "DELETE";
6946
6988
  }
6947
6989
  if (/\b(drop\s+(database|table|schema)|truncate\s+table|kubectl\s+delete|terraform\s+destroy|gcloud\s+(?:sql\s+instances|compute\s+instances|projects|run\s+services)\s+delete|az\s+(?:group|vm|sql\s+server)\s+delete|aws\s+(?:s3\s+rb|rds\s+delete-db-instance|ec2\s+terminate-instances)|docker\s+(?:system\s+prune|volume\s+rm|rm\s+-f)|mkfs|dd\s+if=)\b/.test(value)) {
@@ -6971,6 +7013,7 @@ function classifyShellWords(masked, raw, lead) {
6971
7013
  return "SHELL";
6972
7014
  }
6973
7015
  function classifyShellCommandOperation(command, depth = 0) {
7016
+ if (shellUrlActionText(command) !== command) return "write";
6974
7017
  const segments = splitShellSegments(command);
6975
7018
  if (segments.length === 0) return "SHELL";
6976
7019
  return worstShellOp(segments.map((segment) => classifyShellSegment(segment, depth)), "read");
@@ -84,7 +84,7 @@ const FACT_WORDS = [
84
84
  ? `a secret-shaped value was found in the arguments${f['toolArgs.secretKind'] && f['toolArgs.secretKind'] !== 'none' ? ` (${f['toolArgs.secretKind'].replace(/_/g, ' ')})` : ''}`
85
85
  : undefined],
86
86
  ['toolArgs.containsPii', v => v === 'true' ? 'personal data (PII) was found in the arguments' : undefined],
87
- ['destination.type', (v, f) => v === 'external' ? `the destination ${f['destination.domain'] ? `"${f['destination.domain']}" ` : ''}is outside your organisation` : undefined],
87
+ ['destination.type', (v, f) => v === 'external' ? `the destination ${f['destination.domain'] ? `"${f['destination.domain']}" ` : ''}is classified as external; this does not establish whether your organisation trusts it` : undefined],
88
88
  ['destination.domain', (v, f) => f['destination.type'] === 'external' ? undefined : `destination host: ${v}`],
89
89
  ['url.risk', v => v && v !== 'none' && v !== 'low' ? `the URL was rated ${v} risk` : undefined],
90
90
  ['path.sensitive', v => v === 'true' ? 'the path is a sensitive location' : undefined],
@@ -830,7 +830,7 @@ function prepareDeveloperConfirmation(event, call, payload, source) {
830
830
  // Name the rule in the prompt itself: a developer deciding y/n must see WHICH
831
831
  // rule is asking and where to change it — same builder as every block.
832
832
  const ceilingNote = source.askedInsteadOfBlocked
833
- ? 'This policy is set to Block. On a developer workstation it asks you instead of blocking — a wrong Ask costs one click, a wrong block costs your afternoon. An admin can mark the policy "Hard block" in the console to stop this without asking.'
833
+ ? 'This policy is set to Block. Because it is not marked "Hard block", your workstation requires your approval before continuing. An admin can review the matching rule and its scope in the console.'
834
834
  : undefined;
835
835
  const explained = source.where === 'local-safety' && source.localFinding
836
836
  ? explainLocalSafetyStop('ask', event, call, source.localFinding)
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.34.10"
2
+ "version": "1.34.11"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.34.10",
3
+ "version": "1.34.11",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {