@node9/proxy 1.58.0 → 1.58.2
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/cli.js +69 -22
- package/dist/cli.mjs +69 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -53001,8 +53001,17 @@ function ghHeaders() {
|
|
|
53001
53001
|
return h;
|
|
53002
53002
|
}
|
|
53003
53003
|
async function ghGet(url) {
|
|
53004
|
-
|
|
53005
|
-
|
|
53004
|
+
let res;
|
|
53005
|
+
try {
|
|
53006
|
+
res = await (0, import_undici.request)(url, {
|
|
53007
|
+
headers: ghHeaders(),
|
|
53008
|
+
headersTimeout: 1e4,
|
|
53009
|
+
bodyTimeout: 1e4
|
|
53010
|
+
});
|
|
53011
|
+
} catch {
|
|
53012
|
+
return { status: 0, json: null };
|
|
53013
|
+
}
|
|
53014
|
+
const body = await res.body.text().catch(() => "");
|
|
53006
53015
|
let json = null;
|
|
53007
53016
|
try {
|
|
53008
53017
|
json = JSON.parse(body);
|
|
@@ -53011,6 +53020,7 @@ async function ghGet(url) {
|
|
|
53011
53020
|
return { status: res.statusCode, json };
|
|
53012
53021
|
}
|
|
53013
53022
|
var RATE_LIMIT_NOTE = "GitHub rate limit hit \u2014 results may be INCOMPLETE (a missing file could be unread, not absent). Set GITHUB_TOKEN.";
|
|
53023
|
+
var NETWORK_NOTE = "A network error/timeout occurred \u2014 results may be INCOMPLETE (some files were not fetched).";
|
|
53014
53024
|
async function fetchOne(owner, repo, filePath, notes) {
|
|
53015
53025
|
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`;
|
|
53016
53026
|
const { status, json } = await ghGet(url);
|
|
@@ -53018,6 +53028,10 @@ async function fetchOne(owner, repo, filePath, notes) {
|
|
|
53018
53028
|
if (!notes.includes(RATE_LIMIT_NOTE)) notes.push(RATE_LIMIT_NOTE);
|
|
53019
53029
|
return null;
|
|
53020
53030
|
}
|
|
53031
|
+
if (status === 0) {
|
|
53032
|
+
if (!notes.includes(NETWORK_NOTE)) notes.push(NETWORK_NOTE);
|
|
53033
|
+
return null;
|
|
53034
|
+
}
|
|
53021
53035
|
if (status !== 200 || !json || typeof json !== "object") return null;
|
|
53022
53036
|
const f = json;
|
|
53023
53037
|
if (f.type !== "file" || typeof f.content !== "string") return null;
|
|
@@ -53095,8 +53109,20 @@ async function fetchTree(input, onProgress) {
|
|
|
53095
53109
|
|
|
53096
53110
|
// src/ci-check/workflows.ts
|
|
53097
53111
|
var import_yaml = require("yaml");
|
|
53112
|
+
|
|
53113
|
+
// src/ci-check/types.ts
|
|
53114
|
+
var SEVERITY_RANK2 = {
|
|
53115
|
+
critical: 4,
|
|
53116
|
+
high: 3,
|
|
53117
|
+
medium: 2,
|
|
53118
|
+
advisory: 1
|
|
53119
|
+
};
|
|
53120
|
+
|
|
53121
|
+
// src/ci-check/workflows.ts
|
|
53098
53122
|
var AGENT_ACTION_RE = /(anthropics\/claude-code(-base)?-action|anthropics\/claude-code|openai\/codex|codex-action|run-?aider|aider-?action|google-github-actions\/run-gemini)/i;
|
|
53099
53123
|
var BROAD_TOOL_RE = /(^|["\s,])(Bash|Write|Edit)(\s|,|"|$)|Bash\(\s*\*|Bash\((curl|wget|git push|git commit|git config|git:|rm|sh|bash|eval|npx|pip)/i;
|
|
53124
|
+
var EXFIL_RCE_RE = /(^|["\s,])Bash(\s|,|"|$)|Bash\(\s*\*|Bash\((curl|wget|sh[\s):]|eval|rm[\s):]|git push)/i;
|
|
53125
|
+
var GH_WRITE_TOOL_RE = /Bash\(\s*(gh api|gh:|gh (pr|issue) (comment|edit|merge|close|review|create|ready|lock|reopen)|git push|git:)/i;
|
|
53100
53126
|
function str(v) {
|
|
53101
53127
|
return typeof v === "string" ? v : v == null ? "" : JSON.stringify(v);
|
|
53102
53128
|
}
|
|
@@ -53121,11 +53147,12 @@ function isAgentStep(step) {
|
|
|
53121
53147
|
return false;
|
|
53122
53148
|
}
|
|
53123
53149
|
function collectTools(steps) {
|
|
53150
|
+
const stripDeny = (x) => x.replace(/--disallowed[-_]?tools\s+("[^"]*"|'[^']*'|\S+)/gi, " ").replace(/["']disallowed[_]?[tT]ools["']\s*:\s*\[[^\]]*\]/g, " ");
|
|
53124
53151
|
let s = "";
|
|
53125
53152
|
for (const st of steps) {
|
|
53126
53153
|
const w = st.with ?? {};
|
|
53127
|
-
s += " " + str(w["claude_args"]) + " " + str(w["allowed_tools"]) + " " + str(w["allowedTools"]);
|
|
53128
|
-
s += " " + str(w["settings"]);
|
|
53154
|
+
s += " " + stripDeny(str(w["claude_args"])) + " " + str(w["allowed_tools"]) + " " + str(w["allowedTools"]);
|
|
53155
|
+
s += " " + stripDeny(str(w["settings"]));
|
|
53129
53156
|
}
|
|
53130
53157
|
return s;
|
|
53131
53158
|
}
|
|
@@ -53163,12 +53190,22 @@ function hasActorGate(wf, raw) {
|
|
|
53163
53190
|
const labelGated = !!labeled && /event\.label|label\.name/i.test(ifs);
|
|
53164
53191
|
return gated || labelGated;
|
|
53165
53192
|
}
|
|
53166
|
-
function
|
|
53193
|
+
function hasImplicitActorGate(agentSteps, bypassActive) {
|
|
53194
|
+
if (bypassActive) return false;
|
|
53195
|
+
return agentSteps.some((s) => /anthropics\/claude-code-action@/i.test(s.uses ?? ""));
|
|
53196
|
+
}
|
|
53197
|
+
function permsElevated(wf, agentJobs) {
|
|
53167
53198
|
const check = (p) => {
|
|
53168
53199
|
const s = str(p);
|
|
53169
|
-
return /contents
|
|
53200
|
+
return /["']?(contents|id-token|packages)["']?\s*:\s*["']?write/i.test(s);
|
|
53170
53201
|
};
|
|
53171
|
-
return check(wf.permissions) ||
|
|
53202
|
+
return check(wf.permissions) || agentJobs.some((j) => check(j.permissions));
|
|
53203
|
+
}
|
|
53204
|
+
function hasGithubWritePerm(wf, agentJobs) {
|
|
53205
|
+
const check = (p) => /["']?(contents|pull-requests|issues|packages|actions|deployments)["']?\s*:\s*["']?write/i.test(
|
|
53206
|
+
str(p)
|
|
53207
|
+
);
|
|
53208
|
+
return check(wf.permissions) || agentJobs.some((j) => check(j.permissions));
|
|
53172
53209
|
}
|
|
53173
53210
|
function usesPat(wf) {
|
|
53174
53211
|
for (const { step } of allSteps(wf)) {
|
|
@@ -53202,6 +53239,11 @@ function analyzeWorkflow(path70, content) {
|
|
|
53202
53239
|
const wf = raw;
|
|
53203
53240
|
const steps = allSteps(wf).map((s) => s.step);
|
|
53204
53241
|
const agentSteps = steps.filter(isAgentStep);
|
|
53242
|
+
const agentJobs = [
|
|
53243
|
+
...new Set(
|
|
53244
|
+
allSteps(wf).filter((s) => isAgentStep(s.step)).map((s) => s.job)
|
|
53245
|
+
)
|
|
53246
|
+
];
|
|
53205
53247
|
if (agentSteps.length === 0) return null;
|
|
53206
53248
|
const triggers = triggerKeys(wf, raw);
|
|
53207
53249
|
const secretExposed = triggers.some((t) => /pull_request_target|workflow_run/i.test(t));
|
|
@@ -53209,19 +53251,23 @@ function analyzeWorkflow(path70, content) {
|
|
|
53209
53251
|
const nonWrite = str(agentSteps.map((s) => s.with?.["allowed_non_write_users"]).find(Boolean));
|
|
53210
53252
|
const nonWriteStar = nonWrite === "*";
|
|
53211
53253
|
const nonWriteList = !!nonWrite && nonWrite !== "*";
|
|
53254
|
+
const untrustedTrigger = secretExposed || forkInput;
|
|
53255
|
+
const bypassActive = nonWriteStar && untrustedTrigger;
|
|
53212
53256
|
const head = untrustedHeadCheckout(wf);
|
|
53213
53257
|
const promptUntrusted = promptTakesUntrusted(agentSteps);
|
|
53214
53258
|
const reach = Math.max(
|
|
53215
53259
|
head === "root" ? 3 : head === "subdir" ? 1 : 0,
|
|
53216
53260
|
promptUntrusted ? 2 : 0,
|
|
53217
|
-
|
|
53261
|
+
bypassActive ? 2 : 0
|
|
53218
53262
|
);
|
|
53219
53263
|
const toolsBlob = collectTools(agentSteps);
|
|
53220
53264
|
const broadTools = BROAD_TOOL_RE.test(toolsBlob);
|
|
53221
|
-
const elevated = permsElevated(wf);
|
|
53265
|
+
const elevated = permsElevated(wf, agentJobs);
|
|
53222
53266
|
const pat = usesPat(wf);
|
|
53223
|
-
const power = (broadTools ? 2 : 0) + (
|
|
53224
|
-
const
|
|
53267
|
+
const power = (broadTools ? 2 : 0) + (bypassActive ? 1 : 0) + (elevated ? 1 : 0) + (pat ? 1 : 0);
|
|
53268
|
+
const explicitGate = hasActorGate(wf, raw);
|
|
53269
|
+
const implicitGate = hasImplicitActorGate(agentSteps, bypassActive);
|
|
53270
|
+
const gate = explicitGate || implicitGate;
|
|
53225
53271
|
const envDeny = hasEnvDeny(agentSteps);
|
|
53226
53272
|
const pinned = agentActionsPinned(agentSteps);
|
|
53227
53273
|
let score = reach + power;
|
|
@@ -53231,7 +53277,14 @@ function analyzeWorkflow(path70, content) {
|
|
|
53231
53277
|
score = Math.max(0, score);
|
|
53232
53278
|
if (score === 0 && !secretExposed) return null;
|
|
53233
53279
|
let severity = severityFromScore(score);
|
|
53234
|
-
if (gate &&
|
|
53280
|
+
if (gate && severity && severity !== "advisory") severity = "advisory";
|
|
53281
|
+
if (reach === 0 && severity && severity !== "advisory") severity = "advisory";
|
|
53282
|
+
if (!broadTools && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium)
|
|
53283
|
+
severity = "medium";
|
|
53284
|
+
const exfilOrRce = EXFIL_RCE_RE.test(toolsBlob);
|
|
53285
|
+
const githubWriteTool = GH_WRITE_TOOL_RE.test(toolsBlob);
|
|
53286
|
+
const canDamage = exfilOrRce || (hasGithubWritePerm(wf, agentJobs) || pat) && githubWriteTool;
|
|
53287
|
+
if (!canDamage && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
|
|
53235
53288
|
if (!severity) severity = "advisory";
|
|
53236
53289
|
const signals = [];
|
|
53237
53290
|
if (secretExposed)
|
|
@@ -53243,12 +53296,14 @@ function analyzeWorkflow(path70, content) {
|
|
|
53243
53296
|
if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
|
|
53244
53297
|
if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
|
|
53245
53298
|
if (broadTools) signals.push("agent has broad/write-capable tools (Bash/Write/curl/git push)");
|
|
53246
|
-
if (
|
|
53299
|
+
if (bypassActive) signals.push('allowed_non_write_users: "*" \u2014 any user can trigger the agent');
|
|
53247
53300
|
if (elevated) signals.push("elevated permissions (contents/id-token: write)");
|
|
53248
53301
|
if (pat) signals.push("a static PAT is exposed to the agent (recoverable via injection)");
|
|
53249
53302
|
if (!gate && reach > 0) signals.push("no effective actor gate");
|
|
53250
53303
|
const mitigations = [];
|
|
53251
|
-
if (
|
|
53304
|
+
if (explicitGate) mitigations.push("actor-gated (maintainer/label/write-user required)");
|
|
53305
|
+
else if (implicitGate)
|
|
53306
|
+
mitigations.push("claude-code-action gates the agent to write-access users by default");
|
|
53252
53307
|
if (head === "subdir") mitigations.push("untrusted head isolated in a subdir, not root");
|
|
53253
53308
|
if (envDeny) mitigations.push("secrets env-denied from the agent subprocess");
|
|
53254
53309
|
if (pinned) mitigations.push("agent action pinned to a commit SHA");
|
|
@@ -53375,14 +53430,6 @@ function analyzeMcp(path70, content) {
|
|
|
53375
53430
|
return findings;
|
|
53376
53431
|
}
|
|
53377
53432
|
|
|
53378
|
-
// src/ci-check/types.ts
|
|
53379
|
-
var SEVERITY_RANK2 = {
|
|
53380
|
-
critical: 4,
|
|
53381
|
-
high: 3,
|
|
53382
|
-
medium: 2,
|
|
53383
|
-
advisory: 1
|
|
53384
|
-
};
|
|
53385
|
-
|
|
53386
53433
|
// src/ci-check/index.ts
|
|
53387
53434
|
function worstOf(findings) {
|
|
53388
53435
|
let worst = null;
|
package/dist/cli.mjs
CHANGED
|
@@ -52994,8 +52994,17 @@ function ghHeaders() {
|
|
|
52994
52994
|
return h;
|
|
52995
52995
|
}
|
|
52996
52996
|
async function ghGet(url) {
|
|
52997
|
-
|
|
52998
|
-
|
|
52997
|
+
let res;
|
|
52998
|
+
try {
|
|
52999
|
+
res = await (0, import_undici.request)(url, {
|
|
53000
|
+
headers: ghHeaders(),
|
|
53001
|
+
headersTimeout: 1e4,
|
|
53002
|
+
bodyTimeout: 1e4
|
|
53003
|
+
});
|
|
53004
|
+
} catch {
|
|
53005
|
+
return { status: 0, json: null };
|
|
53006
|
+
}
|
|
53007
|
+
const body = await res.body.text().catch(() => "");
|
|
52999
53008
|
let json = null;
|
|
53000
53009
|
try {
|
|
53001
53010
|
json = JSON.parse(body);
|
|
@@ -53004,6 +53013,7 @@ async function ghGet(url) {
|
|
|
53004
53013
|
return { status: res.statusCode, json };
|
|
53005
53014
|
}
|
|
53006
53015
|
var RATE_LIMIT_NOTE = "GitHub rate limit hit \u2014 results may be INCOMPLETE (a missing file could be unread, not absent). Set GITHUB_TOKEN.";
|
|
53016
|
+
var NETWORK_NOTE = "A network error/timeout occurred \u2014 results may be INCOMPLETE (some files were not fetched).";
|
|
53007
53017
|
async function fetchOne(owner, repo, filePath, notes) {
|
|
53008
53018
|
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`;
|
|
53009
53019
|
const { status, json } = await ghGet(url);
|
|
@@ -53011,6 +53021,10 @@ async function fetchOne(owner, repo, filePath, notes) {
|
|
|
53011
53021
|
if (!notes.includes(RATE_LIMIT_NOTE)) notes.push(RATE_LIMIT_NOTE);
|
|
53012
53022
|
return null;
|
|
53013
53023
|
}
|
|
53024
|
+
if (status === 0) {
|
|
53025
|
+
if (!notes.includes(NETWORK_NOTE)) notes.push(NETWORK_NOTE);
|
|
53026
|
+
return null;
|
|
53027
|
+
}
|
|
53014
53028
|
if (status !== 200 || !json || typeof json !== "object") return null;
|
|
53015
53029
|
const f = json;
|
|
53016
53030
|
if (f.type !== "file" || typeof f.content !== "string") return null;
|
|
@@ -53088,8 +53102,20 @@ async function fetchTree(input, onProgress) {
|
|
|
53088
53102
|
|
|
53089
53103
|
// src/ci-check/workflows.ts
|
|
53090
53104
|
import { parse as parseYaml } from "yaml";
|
|
53105
|
+
|
|
53106
|
+
// src/ci-check/types.ts
|
|
53107
|
+
var SEVERITY_RANK2 = {
|
|
53108
|
+
critical: 4,
|
|
53109
|
+
high: 3,
|
|
53110
|
+
medium: 2,
|
|
53111
|
+
advisory: 1
|
|
53112
|
+
};
|
|
53113
|
+
|
|
53114
|
+
// src/ci-check/workflows.ts
|
|
53091
53115
|
var AGENT_ACTION_RE = /(anthropics\/claude-code(-base)?-action|anthropics\/claude-code|openai\/codex|codex-action|run-?aider|aider-?action|google-github-actions\/run-gemini)/i;
|
|
53092
53116
|
var BROAD_TOOL_RE = /(^|["\s,])(Bash|Write|Edit)(\s|,|"|$)|Bash\(\s*\*|Bash\((curl|wget|git push|git commit|git config|git:|rm|sh|bash|eval|npx|pip)/i;
|
|
53117
|
+
var EXFIL_RCE_RE = /(^|["\s,])Bash(\s|,|"|$)|Bash\(\s*\*|Bash\((curl|wget|sh[\s):]|eval|rm[\s):]|git push)/i;
|
|
53118
|
+
var GH_WRITE_TOOL_RE = /Bash\(\s*(gh api|gh:|gh (pr|issue) (comment|edit|merge|close|review|create|ready|lock|reopen)|git push|git:)/i;
|
|
53093
53119
|
function str(v) {
|
|
53094
53120
|
return typeof v === "string" ? v : v == null ? "" : JSON.stringify(v);
|
|
53095
53121
|
}
|
|
@@ -53114,11 +53140,12 @@ function isAgentStep(step) {
|
|
|
53114
53140
|
return false;
|
|
53115
53141
|
}
|
|
53116
53142
|
function collectTools(steps) {
|
|
53143
|
+
const stripDeny = (x) => x.replace(/--disallowed[-_]?tools\s+("[^"]*"|'[^']*'|\S+)/gi, " ").replace(/["']disallowed[_]?[tT]ools["']\s*:\s*\[[^\]]*\]/g, " ");
|
|
53117
53144
|
let s = "";
|
|
53118
53145
|
for (const st of steps) {
|
|
53119
53146
|
const w = st.with ?? {};
|
|
53120
|
-
s += " " + str(w["claude_args"]) + " " + str(w["allowed_tools"]) + " " + str(w["allowedTools"]);
|
|
53121
|
-
s += " " + str(w["settings"]);
|
|
53147
|
+
s += " " + stripDeny(str(w["claude_args"])) + " " + str(w["allowed_tools"]) + " " + str(w["allowedTools"]);
|
|
53148
|
+
s += " " + stripDeny(str(w["settings"]));
|
|
53122
53149
|
}
|
|
53123
53150
|
return s;
|
|
53124
53151
|
}
|
|
@@ -53156,12 +53183,22 @@ function hasActorGate(wf, raw) {
|
|
|
53156
53183
|
const labelGated = !!labeled && /event\.label|label\.name/i.test(ifs);
|
|
53157
53184
|
return gated || labelGated;
|
|
53158
53185
|
}
|
|
53159
|
-
function
|
|
53186
|
+
function hasImplicitActorGate(agentSteps, bypassActive) {
|
|
53187
|
+
if (bypassActive) return false;
|
|
53188
|
+
return agentSteps.some((s) => /anthropics\/claude-code-action@/i.test(s.uses ?? ""));
|
|
53189
|
+
}
|
|
53190
|
+
function permsElevated(wf, agentJobs) {
|
|
53160
53191
|
const check = (p) => {
|
|
53161
53192
|
const s = str(p);
|
|
53162
|
-
return /contents
|
|
53193
|
+
return /["']?(contents|id-token|packages)["']?\s*:\s*["']?write/i.test(s);
|
|
53163
53194
|
};
|
|
53164
|
-
return check(wf.permissions) ||
|
|
53195
|
+
return check(wf.permissions) || agentJobs.some((j) => check(j.permissions));
|
|
53196
|
+
}
|
|
53197
|
+
function hasGithubWritePerm(wf, agentJobs) {
|
|
53198
|
+
const check = (p) => /["']?(contents|pull-requests|issues|packages|actions|deployments)["']?\s*:\s*["']?write/i.test(
|
|
53199
|
+
str(p)
|
|
53200
|
+
);
|
|
53201
|
+
return check(wf.permissions) || agentJobs.some((j) => check(j.permissions));
|
|
53165
53202
|
}
|
|
53166
53203
|
function usesPat(wf) {
|
|
53167
53204
|
for (const { step } of allSteps(wf)) {
|
|
@@ -53195,6 +53232,11 @@ function analyzeWorkflow(path70, content) {
|
|
|
53195
53232
|
const wf = raw;
|
|
53196
53233
|
const steps = allSteps(wf).map((s) => s.step);
|
|
53197
53234
|
const agentSteps = steps.filter(isAgentStep);
|
|
53235
|
+
const agentJobs = [
|
|
53236
|
+
...new Set(
|
|
53237
|
+
allSteps(wf).filter((s) => isAgentStep(s.step)).map((s) => s.job)
|
|
53238
|
+
)
|
|
53239
|
+
];
|
|
53198
53240
|
if (agentSteps.length === 0) return null;
|
|
53199
53241
|
const triggers = triggerKeys(wf, raw);
|
|
53200
53242
|
const secretExposed = triggers.some((t) => /pull_request_target|workflow_run/i.test(t));
|
|
@@ -53202,19 +53244,23 @@ function analyzeWorkflow(path70, content) {
|
|
|
53202
53244
|
const nonWrite = str(agentSteps.map((s) => s.with?.["allowed_non_write_users"]).find(Boolean));
|
|
53203
53245
|
const nonWriteStar = nonWrite === "*";
|
|
53204
53246
|
const nonWriteList = !!nonWrite && nonWrite !== "*";
|
|
53247
|
+
const untrustedTrigger = secretExposed || forkInput;
|
|
53248
|
+
const bypassActive = nonWriteStar && untrustedTrigger;
|
|
53205
53249
|
const head = untrustedHeadCheckout(wf);
|
|
53206
53250
|
const promptUntrusted = promptTakesUntrusted(agentSteps);
|
|
53207
53251
|
const reach = Math.max(
|
|
53208
53252
|
head === "root" ? 3 : head === "subdir" ? 1 : 0,
|
|
53209
53253
|
promptUntrusted ? 2 : 0,
|
|
53210
|
-
|
|
53254
|
+
bypassActive ? 2 : 0
|
|
53211
53255
|
);
|
|
53212
53256
|
const toolsBlob = collectTools(agentSteps);
|
|
53213
53257
|
const broadTools = BROAD_TOOL_RE.test(toolsBlob);
|
|
53214
|
-
const elevated = permsElevated(wf);
|
|
53258
|
+
const elevated = permsElevated(wf, agentJobs);
|
|
53215
53259
|
const pat = usesPat(wf);
|
|
53216
|
-
const power = (broadTools ? 2 : 0) + (
|
|
53217
|
-
const
|
|
53260
|
+
const power = (broadTools ? 2 : 0) + (bypassActive ? 1 : 0) + (elevated ? 1 : 0) + (pat ? 1 : 0);
|
|
53261
|
+
const explicitGate = hasActorGate(wf, raw);
|
|
53262
|
+
const implicitGate = hasImplicitActorGate(agentSteps, bypassActive);
|
|
53263
|
+
const gate = explicitGate || implicitGate;
|
|
53218
53264
|
const envDeny = hasEnvDeny(agentSteps);
|
|
53219
53265
|
const pinned = agentActionsPinned(agentSteps);
|
|
53220
53266
|
let score = reach + power;
|
|
@@ -53224,7 +53270,14 @@ function analyzeWorkflow(path70, content) {
|
|
|
53224
53270
|
score = Math.max(0, score);
|
|
53225
53271
|
if (score === 0 && !secretExposed) return null;
|
|
53226
53272
|
let severity = severityFromScore(score);
|
|
53227
|
-
if (gate &&
|
|
53273
|
+
if (gate && severity && severity !== "advisory") severity = "advisory";
|
|
53274
|
+
if (reach === 0 && severity && severity !== "advisory") severity = "advisory";
|
|
53275
|
+
if (!broadTools && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium)
|
|
53276
|
+
severity = "medium";
|
|
53277
|
+
const exfilOrRce = EXFIL_RCE_RE.test(toolsBlob);
|
|
53278
|
+
const githubWriteTool = GH_WRITE_TOOL_RE.test(toolsBlob);
|
|
53279
|
+
const canDamage = exfilOrRce || (hasGithubWritePerm(wf, agentJobs) || pat) && githubWriteTool;
|
|
53280
|
+
if (!canDamage && severity && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
|
|
53228
53281
|
if (!severity) severity = "advisory";
|
|
53229
53282
|
const signals = [];
|
|
53230
53283
|
if (secretExposed)
|
|
@@ -53236,12 +53289,14 @@ function analyzeWorkflow(path70, content) {
|
|
|
53236
53289
|
if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
|
|
53237
53290
|
if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
|
|
53238
53291
|
if (broadTools) signals.push("agent has broad/write-capable tools (Bash/Write/curl/git push)");
|
|
53239
|
-
if (
|
|
53292
|
+
if (bypassActive) signals.push('allowed_non_write_users: "*" \u2014 any user can trigger the agent');
|
|
53240
53293
|
if (elevated) signals.push("elevated permissions (contents/id-token: write)");
|
|
53241
53294
|
if (pat) signals.push("a static PAT is exposed to the agent (recoverable via injection)");
|
|
53242
53295
|
if (!gate && reach > 0) signals.push("no effective actor gate");
|
|
53243
53296
|
const mitigations = [];
|
|
53244
|
-
if (
|
|
53297
|
+
if (explicitGate) mitigations.push("actor-gated (maintainer/label/write-user required)");
|
|
53298
|
+
else if (implicitGate)
|
|
53299
|
+
mitigations.push("claude-code-action gates the agent to write-access users by default");
|
|
53245
53300
|
if (head === "subdir") mitigations.push("untrusted head isolated in a subdir, not root");
|
|
53246
53301
|
if (envDeny) mitigations.push("secrets env-denied from the agent subprocess");
|
|
53247
53302
|
if (pinned) mitigations.push("agent action pinned to a commit SHA");
|
|
@@ -53368,14 +53423,6 @@ function analyzeMcp(path70, content) {
|
|
|
53368
53423
|
return findings;
|
|
53369
53424
|
}
|
|
53370
53425
|
|
|
53371
|
-
// src/ci-check/types.ts
|
|
53372
|
-
var SEVERITY_RANK2 = {
|
|
53373
|
-
critical: 4,
|
|
53374
|
-
high: 3,
|
|
53375
|
-
medium: 2,
|
|
53376
|
-
advisory: 1
|
|
53377
|
-
};
|
|
53378
|
-
|
|
53379
53426
|
// src/ci-check/index.ts
|
|
53380
53427
|
function worstOf(findings) {
|
|
53381
53428
|
let worst = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.58.
|
|
3
|
+
"version": "1.58.2",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|