fullcourtdefense-cli 1.34.9 → 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.
- package/dist/actionPolicyEngine.js +47 -3
- package/dist/blockExplanation.js +1 -1
- package/dist/commands/hook.js +1 -1
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -6532,6 +6532,7 @@ function isHighEntropySecretToken(token, allowWordSlug = false) {
|
|
|
6532
6532
|
if (/^data:/i.test(token) || /^[A-Za-z0-9+/]{200,}={0,2}$/.test(token)) return false;
|
|
6533
6533
|
if (/^(?:https?|s3|gs|file):\/\//i.test(token) || /[\\/]{1}[\w.-]+[\\/]/.test(token)) return false;
|
|
6534
6534
|
if (/^[a-z]+(?:[A-Z][a-z0-9]+)+$/.test(token) || /^[a-z]+(?:_[a-z0-9]+)+$/.test(token)) return false;
|
|
6535
|
+
if (allowWordSlug && /^[A-Z][a-z]+-(?:[A-Z][a-z]+){2,}$/.test(token)) return false;
|
|
6535
6536
|
if (allowWordSlug && /^[a-z]+(?:-[a-z]+)+(?:-\d+)?$/.test(token)) return false;
|
|
6536
6537
|
let classes = 0;
|
|
6537
6538
|
if (/[a-z]/.test(token)) classes++;
|
|
@@ -6644,13 +6645,55 @@ function extractUrl(args, _argsText) {
|
|
|
6644
6645
|
const direct = args.url || args.uri || args.endpoint || args.webhookUrl || args.callbackUrl || args.href || args.link;
|
|
6645
6646
|
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
6646
6647
|
for (const field of URL_ACTION_FIELDS) {
|
|
6647
|
-
const
|
|
6648
|
-
if (typeof
|
|
6648
|
+
const raw = args[field];
|
|
6649
|
+
if (typeof raw !== "string") continue;
|
|
6650
|
+
const value = ["command", "cmd", "script", "input"].includes(field) ? shellUrlActionText(raw) : raw;
|
|
6649
6651
|
const found = value.match(/https?:\/\/[^\s"'<>]+/i)?.[0] || value.match(/\b(?:s3|gs|ftp|sftp|smb):\/\/[^\s"'<>]+/i)?.[0];
|
|
6650
6652
|
if (found) return found;
|
|
6651
6653
|
}
|
|
6652
6654
|
return "";
|
|
6653
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
|
+
}
|
|
6654
6697
|
function splitShellSegments(command, alsoOnPipe = false) {
|
|
6655
6698
|
const out = [];
|
|
6656
6699
|
let cur = "";
|
|
@@ -6940,7 +6983,7 @@ function classifyShellWords(masked, raw, lead) {
|
|
|
6940
6983
|
if (lead === "gh" && /\bgh\s+(?:pr|issue|repo|run|release|workflow)\s+(?:view|list|status|checks|diff|download)\b/.test(value)) return "read";
|
|
6941
6984
|
if (lead === "gh" && /\bgh\s+auth\s+status\b/.test(value) && !/--show-token|\s-t\b/.test(value)) return "read";
|
|
6942
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";
|
|
6943
|
-
if (/\b(rm\s+-[a-z]*r[a-z]*f|rm\s+-[a-z]*f[a-z]*r|del\s+\/[fqs]|remove-item\b.*\
|
|
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)) {
|
|
6944
6987
|
return isWorkspaceScopedRemoval(segment) ? "write" : "DELETE";
|
|
6945
6988
|
}
|
|
6946
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)) {
|
|
@@ -6970,6 +7013,7 @@ function classifyShellWords(masked, raw, lead) {
|
|
|
6970
7013
|
return "SHELL";
|
|
6971
7014
|
}
|
|
6972
7015
|
function classifyShellCommandOperation(command, depth = 0) {
|
|
7016
|
+
if (shellUrlActionText(command) !== command) return "write";
|
|
6973
7017
|
const segments = splitShellSegments(command);
|
|
6974
7018
|
if (segments.length === 0) return "SHELL";
|
|
6975
7019
|
return worstShellOp(segments.map((segment) => classifyShellSegment(segment, depth)), "read");
|
package/dist/blockExplanation.js
CHANGED
|
@@ -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
|
|
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],
|
package/dist/commands/hook.js
CHANGED
|
@@ -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.
|
|
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