proof-pr 0.1.5 → 0.1.6
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/README.md +1 -1
- package/dist/index.js +231 -30
- package/package.json +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ ProofPR 帮助维护者在投入深入 review 之前,先检查 PR 的证据、
|
|
|
9
9
|
作为 GitHub Action 使用时,ProofPR 默认在 PR 打开、PR 分支更新、PR 重新打开时运行。普通分支 push 不会单独生成报告。
|
|
10
10
|
|
|
11
11
|
报告会出现在 PR 评论区、GitHub Actions job summary 和 PR checks 状态里。
|
|
12
|
-
`v0.1.5` 起还可以输出 GitHub annotations,并通过 `sarif-output` 写出 SARIF
|
|
12
|
+
`v0.1.5` 起还可以输出 GitHub annotations,并通过 `sarif-output` 写出 SARIF 文件。当前版本还会识别依赖大版本升级、包生命周期脚本和 `pull_request_target` workflow 触发器。
|
|
13
13
|
|
|
14
14
|
## 使用
|
|
15
15
|
|
package/dist/index.js
CHANGED
|
@@ -23234,9 +23234,10 @@ const configSchema = object({
|
|
|
23234
23234
|
secrets: object({ enabled: schemas_boolean().default(true) }).default({ enabled: true }),
|
|
23235
23235
|
dependencies: object({
|
|
23236
23236
|
flagNewPackages: schemas_boolean().default(true),
|
|
23237
|
-
flagMajorUpgrades: schemas_boolean().default(true)
|
|
23237
|
+
flagMajorUpgrades: schemas_boolean().default(true),
|
|
23238
|
+
flagLifecycleScripts: schemas_boolean().default(true)
|
|
23238
23239
|
})
|
|
23239
|
-
.default({ flagNewPackages: true, flagMajorUpgrades: true }),
|
|
23240
|
+
.default({ flagNewPackages: true, flagMajorUpgrades: true, flagLifecycleScripts: true }),
|
|
23240
23241
|
comment: object({ enabled: schemas_boolean().default(true) }).default({ enabled: true })
|
|
23241
23242
|
});
|
|
23242
23243
|
function parseConfig(input) {
|
|
@@ -23543,6 +23544,21 @@ function maintainerFocus(findings, locale) {
|
|
|
23543
23544
|
? "要求拆分 PR,或提供逐文件 review 指南。"
|
|
23544
23545
|
: "Request a smaller PR or a file-by-file review guide.");
|
|
23545
23546
|
}
|
|
23547
|
+
else if (finding.ruleId === "dependency-major-upgrade") {
|
|
23548
|
+
focus.add(locale === "zh-CN"
|
|
23549
|
+
? "重点核查依赖大版本升级的迁移说明、兼容性和测试覆盖。"
|
|
23550
|
+
: "Review dependency major upgrade migration notes, compatibility, and test coverage.");
|
|
23551
|
+
}
|
|
23552
|
+
else if (finding.ruleId === "dependency-lifecycle-script") {
|
|
23553
|
+
focus.add(locale === "zh-CN"
|
|
23554
|
+
? "合并前审查包生命周期脚本是否会在安装或发布时执行非预期代码。"
|
|
23555
|
+
: "Review package lifecycle scripts for unexpected install or publish-time execution.");
|
|
23556
|
+
}
|
|
23557
|
+
else if (finding.ruleId === "workflow-dangerous-trigger") {
|
|
23558
|
+
focus.add(locale === "zh-CN"
|
|
23559
|
+
? "重点审查 pull_request_target 是否会用高权限 token 执行不可信 PR 代码。"
|
|
23560
|
+
: "Review whether pull_request_target can execute untrusted PR code with privileged tokens.");
|
|
23561
|
+
}
|
|
23546
23562
|
else if (finding.ruleId === "mcp-credential-risk") {
|
|
23547
23563
|
focus.add(locale === "zh-CN"
|
|
23548
23564
|
? "重点审查 MCP command、args 和凭证处理方式。"
|
|
@@ -23604,6 +23620,20 @@ function translateFinding(finding) {
|
|
|
23604
23620
|
recommendation: "请确认包名、许可证、来源可信度,以及 lockfile 是否匹配预期依赖变化。"
|
|
23605
23621
|
};
|
|
23606
23622
|
}
|
|
23623
|
+
if (finding.ruleId === "dependency-major-upgrade") {
|
|
23624
|
+
return {
|
|
23625
|
+
title: "依赖发生大版本升级",
|
|
23626
|
+
message: finding.path ? `${finding.path} 中有依赖跨越了大版本边界。` : finding.message,
|
|
23627
|
+
recommendation: "请核查 changelog、迁移说明、peer dependencies 影响,以及测试是否覆盖升级后的关键路径。"
|
|
23628
|
+
};
|
|
23629
|
+
}
|
|
23630
|
+
if (finding.ruleId === "dependency-lifecycle-script") {
|
|
23631
|
+
return {
|
|
23632
|
+
title: "包生命周期脚本发生变更",
|
|
23633
|
+
message: finding.path ? `${finding.path} 新增或修改了安装/发布阶段可能自动执行的脚本。` : finding.message,
|
|
23634
|
+
recommendation: "请确认该脚本是否必要,是否下载或执行远程代码,以及是否会影响安装该包的用户。"
|
|
23635
|
+
};
|
|
23636
|
+
}
|
|
23607
23637
|
if (finding.ruleId === "workflow-permission-change") {
|
|
23608
23638
|
return {
|
|
23609
23639
|
title: "Workflow 权限发生变更",
|
|
@@ -23611,6 +23641,13 @@ function translateFinding(finding) {
|
|
|
23611
23641
|
recommendation: "请确认 workflow 是否真的需要写权限或 token 权限,并检查不可信 PR 是否能触达该 workflow。"
|
|
23612
23642
|
};
|
|
23613
23643
|
}
|
|
23644
|
+
if (finding.ruleId === "workflow-dangerous-trigger") {
|
|
23645
|
+
return {
|
|
23646
|
+
title: "Workflow 使用了 pull_request_target",
|
|
23647
|
+
message: finding.path ? `${finding.path} 新增了 pull_request_target 触发器。` : finding.message,
|
|
23648
|
+
recommendation: "请确认该 workflow 不会用高权限 token、secret 或写权限执行不可信 PR 代码。"
|
|
23649
|
+
};
|
|
23650
|
+
}
|
|
23614
23651
|
if (finding.ruleId === "mcp-credential-risk") {
|
|
23615
23652
|
return {
|
|
23616
23653
|
title: "MCP 配置需要重点审查",
|
|
@@ -23687,9 +23724,12 @@ function translateReviewActionTitle(actionId, fallback) {
|
|
|
23687
23724
|
"add-reproduction-context": "要求补充复现或 before/after 上下文",
|
|
23688
23725
|
"rotate-secret": "轮换并移除暴露的凭证",
|
|
23689
23726
|
"justify-workflow-permissions": "要求说明 workflow 权限最小化理由",
|
|
23727
|
+
"review-privileged-pr-trigger": "审查 pull_request_target 高权限触发器",
|
|
23728
|
+
"review-package-lifecycle-script": "审查包生命周期脚本",
|
|
23690
23729
|
"review-mcp-execution-surface": "审查 MCP 命令、参数和凭证处理",
|
|
23691
23730
|
"request-review-map-or-split": "要求拆分 PR 或提供逐文件 review map",
|
|
23692
23731
|
"verify-dependency-change": "核查依赖来源和 lockfile 影响",
|
|
23732
|
+
"review-major-dependency-upgrade": "核查依赖大版本升级影响",
|
|
23693
23733
|
"assign-sensitive-file-review": "安排敏感文件重点 review"
|
|
23694
23734
|
}[actionId] ?? fallback;
|
|
23695
23735
|
}
|
|
@@ -23704,9 +23744,12 @@ function translateReviewActionDetail(actionId, fallback) {
|
|
|
23704
23744
|
"add-reproduction-context": "PR 应包含复现步骤、预期/实际行为,或相关 before/after 截图。",
|
|
23705
23745
|
"rotate-secret": "在 secret 从 PR 中移除并完成轮换前,不要合并。",
|
|
23706
23746
|
"justify-workflow-permissions": "确认写权限或 OIDC 是否必要,并检查不可信 PR 是否能触发该 workflow。",
|
|
23747
|
+
"review-privileged-pr-trigger": "确认 workflow 不会用写权限 token、secret 或仓库权限执行不可信 PR 代码。",
|
|
23748
|
+
"review-package-lifecycle-script": "检查 install、postinstall、prepare 或 publish 脚本是否会执行非预期代码。",
|
|
23707
23749
|
"review-mcp-execution-surface": "检查 MCP 配置是否提交凭证,或意外扩大本地执行面。",
|
|
23708
23750
|
"request-review-map-or-split": "要求贡献者拆分无关改动,或标出最需要重点 review 的文件。",
|
|
23709
23751
|
"verify-dependency-change": "检查包名、维护者、许可证、安装脚本,以及 lockfile 是否符合预期依赖变化。",
|
|
23752
|
+
"review-major-dependency-upgrade": "检查 changelog、迁移说明、peer dependencies,以及测试是否覆盖升级后的关键路径。",
|
|
23710
23753
|
"assign-sensitive-file-review": "合并前由维护者有意识地检查敏感文件改动。"
|
|
23711
23754
|
}[actionId] ?? fallback;
|
|
23712
23755
|
}
|
|
@@ -23715,7 +23758,10 @@ function translateFocusReason(reasonId, fallback) {
|
|
|
23715
23758
|
"change-size": "review 面积相关 finding",
|
|
23716
23759
|
"sensitive-path": "敏感路径发生变更",
|
|
23717
23760
|
"dependency-added": "依赖清单发生变更",
|
|
23761
|
+
"dependency-major-upgrade": "依赖发生大版本升级",
|
|
23762
|
+
"dependency-lifecycle-script": "包生命周期脚本发生变更",
|
|
23718
23763
|
"workflow-permission-change": "workflow 权限发生变更",
|
|
23764
|
+
"workflow-dangerous-trigger": "workflow 使用了高风险触发器",
|
|
23719
23765
|
"mcp-credential-risk": "MCP 配置存在执行面或凭证风险",
|
|
23720
23766
|
"missing-tests": "代码改动缺少测试或验证证据"
|
|
23721
23767
|
}[reasonId] ?? fallback;
|
|
@@ -23744,6 +23790,9 @@ function translateDeduction(reasonId, fallback) {
|
|
|
23744
23790
|
"sensitive-path-high": "高敏感文件发生变更,需要重点 review。",
|
|
23745
23791
|
"sensitive-path-medium": "敏感文件发生变更,需要重点 review。",
|
|
23746
23792
|
"dependency-change": "依赖清单发生变更。",
|
|
23793
|
+
"dependency-major-upgrade": "依赖发生大版本升级。",
|
|
23794
|
+
"dependency-lifecycle-script": "包生命周期脚本可能在安装或发布阶段执行代码。",
|
|
23795
|
+
"workflow-dangerous-trigger": "pull_request_target workflow 需要重点审查高权限触发路径。",
|
|
23747
23796
|
"missing-tests": "代码发生变更,但缺少测试变更或验证说明。"
|
|
23748
23797
|
}[reasonId] ?? fallback;
|
|
23749
23798
|
}
|
|
@@ -24055,6 +24104,7 @@ function analyzeDiffFiles(files, config, pullRequest) {
|
|
|
24055
24104
|
findings.push(...analyzePullRequestEvidence(activeFiles, pullRequest));
|
|
24056
24105
|
findings.push(...analyzeDependencyChanges(activeFiles, config));
|
|
24057
24106
|
findings.push(...analyzeWorkflowPermissions(activeFiles));
|
|
24107
|
+
findings.push(...analyzeWorkflowDangerousTriggers(activeFiles));
|
|
24058
24108
|
findings.push(...analyzeMcpConfigs(activeFiles));
|
|
24059
24109
|
if (config.secrets.enabled) {
|
|
24060
24110
|
for (const file of activeFiles) {
|
|
@@ -24180,49 +24230,132 @@ function analyzePullRequestEvidence(files, pullRequest) {
|
|
|
24180
24230
|
return findings;
|
|
24181
24231
|
}
|
|
24182
24232
|
function analyzeDependencyChanges(files, config) {
|
|
24183
|
-
if (!config.dependencies.flagNewPackages) {
|
|
24184
|
-
return [];
|
|
24185
|
-
}
|
|
24186
24233
|
const findings = [];
|
|
24187
24234
|
for (const file of files.filter((candidate) => isDependencyManifest(candidate.path))) {
|
|
24188
|
-
|
|
24189
|
-
|
|
24190
|
-
|
|
24235
|
+
if (config.dependencies.flagNewPackages) {
|
|
24236
|
+
const addedDependencyLines = file.addedLines.filter((line) => isDependencyLikeAddition(file.path, line.value.trim()));
|
|
24237
|
+
if (addedDependencyLines.length > 0) {
|
|
24238
|
+
findings.push({
|
|
24239
|
+
ruleId: "dependency-added",
|
|
24240
|
+
title: "Dependency manifest changed",
|
|
24241
|
+
message: `${file.path} adds or changes dependency-like entries.`,
|
|
24242
|
+
severity: "medium",
|
|
24243
|
+
path: file.path,
|
|
24244
|
+
evidence: addedDependencyLines.slice(0, 5).map(formatEvidenceLine),
|
|
24245
|
+
recommendation: "Verify package names, licenses, provenance, and whether the lockfile matches the intended dependency change."
|
|
24246
|
+
});
|
|
24247
|
+
}
|
|
24248
|
+
}
|
|
24249
|
+
if (config.dependencies.flagMajorUpgrades) {
|
|
24250
|
+
findings.push(...analyzeMajorDependencyUpgrades(file));
|
|
24251
|
+
}
|
|
24252
|
+
if (config.dependencies.flagLifecycleScripts) {
|
|
24253
|
+
findings.push(...analyzeLifecycleScripts(file));
|
|
24191
24254
|
}
|
|
24192
|
-
findings.push({
|
|
24193
|
-
ruleId: "dependency-added",
|
|
24194
|
-
title: "Dependency manifest changed",
|
|
24195
|
-
message: `${file.path} adds or changes dependency-like entries.`,
|
|
24196
|
-
severity: "medium",
|
|
24197
|
-
path: file.path,
|
|
24198
|
-
evidence: addedDependencyLines.slice(0, 5).map(formatEvidenceLine),
|
|
24199
|
-
recommendation: "Verify package names, licenses, provenance, and whether the lockfile matches the intended dependency change."
|
|
24200
|
-
});
|
|
24201
24255
|
}
|
|
24202
24256
|
return findings;
|
|
24203
24257
|
}
|
|
24204
24258
|
function isDependencyLikeAddition(path, line) {
|
|
24259
|
+
return parseDependencyLine(path, { value: line }) !== undefined;
|
|
24260
|
+
}
|
|
24261
|
+
function analyzeMajorDependencyUpgrades(file) {
|
|
24262
|
+
const removedDependencies = new Map();
|
|
24263
|
+
for (const line of file.removedLines) {
|
|
24264
|
+
const parsed = parseDependencyLine(file.path, line);
|
|
24265
|
+
if (parsed) {
|
|
24266
|
+
removedDependencies.set(parsed.name, parsed);
|
|
24267
|
+
}
|
|
24268
|
+
}
|
|
24269
|
+
const upgrades = file.addedLines
|
|
24270
|
+
.map((line) => parseDependencyLine(file.path, line))
|
|
24271
|
+
.filter((line) => Boolean(line))
|
|
24272
|
+
.map((added) => ({ added, removed: removedDependencies.get(added.name) }))
|
|
24273
|
+
.filter((change) => change.removed !== undefined && isMajorUpgrade(change.removed.version, change.added.version));
|
|
24274
|
+
if (upgrades.length === 0) {
|
|
24275
|
+
return [];
|
|
24276
|
+
}
|
|
24277
|
+
return [
|
|
24278
|
+
{
|
|
24279
|
+
ruleId: "dependency-major-upgrade",
|
|
24280
|
+
title: "Dependency major version upgrade",
|
|
24281
|
+
message: `${file.path} upgrades one or more dependencies across a major version boundary.`,
|
|
24282
|
+
severity: "medium",
|
|
24283
|
+
path: file.path,
|
|
24284
|
+
evidence: upgrades.slice(0, 5).map(({ added, removed }) => `${added.line.lineNumber ? `line ${added.line.lineNumber}: ` : ""}${added.name} ${removed.version} -> ${added.version}`),
|
|
24285
|
+
recommendation: "Check changelogs, migration notes, peer dependency impact, and whether tests cover the upgraded package surface."
|
|
24286
|
+
}
|
|
24287
|
+
];
|
|
24288
|
+
}
|
|
24289
|
+
function analyzeLifecycleScripts(file) {
|
|
24290
|
+
if (!file.path.endsWith("package.json")) {
|
|
24291
|
+
return [];
|
|
24292
|
+
}
|
|
24293
|
+
const lifecycleLines = file.addedLines.filter((line) => /^"(?:preinstall|install|postinstall|prepare|prepublish|prepublishOnly)"\s*:/.test(line.value.trim()));
|
|
24294
|
+
if (lifecycleLines.length === 0) {
|
|
24295
|
+
return [];
|
|
24296
|
+
}
|
|
24297
|
+
return [
|
|
24298
|
+
{
|
|
24299
|
+
ruleId: "dependency-lifecycle-script",
|
|
24300
|
+
title: "Package lifecycle script changed",
|
|
24301
|
+
message: `${file.path} adds or changes npm lifecycle scripts that may run during install or publish.`,
|
|
24302
|
+
severity: "high",
|
|
24303
|
+
path: file.path,
|
|
24304
|
+
evidence: lifecycleLines.slice(0, 5).map(formatEvidenceLine),
|
|
24305
|
+
recommendation: "Review whether the lifecycle script is necessary, whether it downloads or executes remote code, and whether it can affect consumers during install."
|
|
24306
|
+
}
|
|
24307
|
+
];
|
|
24308
|
+
}
|
|
24309
|
+
function parseDependencyLine(path, line) {
|
|
24310
|
+
const value = line.value.trim();
|
|
24205
24311
|
if (path.endsWith("package.json")) {
|
|
24206
|
-
const match = /^"(?<key>[@A-Za-z0-9_.-]+)"\s*:\s*"(?<
|
|
24312
|
+
const match = /^"(?<key>[@A-Za-z0-9_.-]+)"\s*:\s*"(?<version>[^"]*)"/.exec(value);
|
|
24207
24313
|
if (!match?.groups) {
|
|
24208
|
-
return
|
|
24314
|
+
return undefined;
|
|
24209
24315
|
}
|
|
24210
|
-
const { key,
|
|
24211
|
-
if (!key || !
|
|
24212
|
-
return
|
|
24316
|
+
const { key, version } = match.groups;
|
|
24317
|
+
if (!key || !version || PACKAGE_JSON_NON_DEPENDENCY_KEYS.has(key)) {
|
|
24318
|
+
return undefined;
|
|
24319
|
+
}
|
|
24320
|
+
if (!/^(?:\^|~|>=?|<=?|\d|workspace:|npm:|file:|link:|portal:|git\+|https?:|github:)/.test(version)) {
|
|
24321
|
+
return undefined;
|
|
24213
24322
|
}
|
|
24214
|
-
return
|
|
24323
|
+
return { name: key, version, line };
|
|
24215
24324
|
}
|
|
24216
24325
|
if (path.endsWith("requirements.txt")) {
|
|
24217
|
-
|
|
24326
|
+
const match = /^(?<name>[A-Za-z0-9_.-]+)(?:\[.*\])?\s*(?:==|>=|<=|~=|>|<)\s*(?<version>[^#\s]+)/.exec(value);
|
|
24327
|
+
return match?.groups?.name && match.groups.version
|
|
24328
|
+
? { name: match.groups.name, version: match.groups.version, line }
|
|
24329
|
+
: undefined;
|
|
24218
24330
|
}
|
|
24219
24331
|
if (path.endsWith("pyproject.toml") || path.endsWith("Cargo.toml")) {
|
|
24220
|
-
|
|
24332
|
+
const match = /^(?<name>[A-Za-z0-9_.-]+)\s*=\s*"(?<version>(?:\^|~|>=?|<=?|\d|workspace:|path\s*=|git\s*=)[^"]*)"/.exec(value);
|
|
24333
|
+
return match?.groups?.name && match.groups.version
|
|
24334
|
+
? { name: match.groups.name, version: match.groups.version, line }
|
|
24335
|
+
: undefined;
|
|
24221
24336
|
}
|
|
24222
24337
|
if (path.endsWith("go.mod")) {
|
|
24223
|
-
|
|
24338
|
+
const match = /^(?:require\s+)?(?<name>[A-Za-z0-9_.\-/]+)\s+(?<version>v\d+\.\d+\.\d+)/.exec(value);
|
|
24339
|
+
return match?.groups?.name && match.groups.version
|
|
24340
|
+
? { name: match.groups.name, version: match.groups.version, line }
|
|
24341
|
+
: undefined;
|
|
24224
24342
|
}
|
|
24225
|
-
return
|
|
24343
|
+
return undefined;
|
|
24344
|
+
}
|
|
24345
|
+
function isMajorUpgrade(previousVersion, nextVersion) {
|
|
24346
|
+
const previousMajor = extractMajorVersion(previousVersion);
|
|
24347
|
+
const nextMajor = extractMajorVersion(nextVersion);
|
|
24348
|
+
return previousMajor !== undefined && nextMajor !== undefined && nextMajor > previousMajor;
|
|
24349
|
+
}
|
|
24350
|
+
function extractMajorVersion(version) {
|
|
24351
|
+
const normalized = version
|
|
24352
|
+
.replace(/^workspace:/, "")
|
|
24353
|
+
.replace(/^npm:[^@]+@/, "")
|
|
24354
|
+
.replace(/^[~^<>=\s]+/, "")
|
|
24355
|
+
.replace(/^v/, "");
|
|
24356
|
+
const match = /(?<major>\d+)\.\d+\.\d+/.exec(normalized);
|
|
24357
|
+
const major = match?.groups?.major ? Number(match.groups.major) : undefined;
|
|
24358
|
+
return major !== undefined && Number.isInteger(major) ? major : undefined;
|
|
24226
24359
|
}
|
|
24227
24360
|
function analyzeWorkflowPermissions(files) {
|
|
24228
24361
|
const findings = [];
|
|
@@ -24243,6 +24376,25 @@ function analyzeWorkflowPermissions(files) {
|
|
|
24243
24376
|
}
|
|
24244
24377
|
return findings;
|
|
24245
24378
|
}
|
|
24379
|
+
function analyzeWorkflowDangerousTriggers(files) {
|
|
24380
|
+
const findings = [];
|
|
24381
|
+
for (const file of files.filter((candidate) => isWorkflowPath(candidate.path))) {
|
|
24382
|
+
const triggerLines = file.addedLines.filter((line) => /\bpull_request_target\b/.test(line.value.trim()));
|
|
24383
|
+
if (triggerLines.length === 0) {
|
|
24384
|
+
continue;
|
|
24385
|
+
}
|
|
24386
|
+
findings.push({
|
|
24387
|
+
ruleId: "workflow-dangerous-trigger",
|
|
24388
|
+
title: "Workflow uses pull_request_target",
|
|
24389
|
+
message: `${file.path} adds pull_request_target, which runs with base repository context and can be risky for untrusted PRs.`,
|
|
24390
|
+
severity: "high",
|
|
24391
|
+
path: file.path,
|
|
24392
|
+
evidence: triggerLines.slice(0, 5).map(formatEvidenceLine),
|
|
24393
|
+
recommendation: "Confirm the workflow does not check out or execute untrusted PR code with privileged tokens or write permissions."
|
|
24394
|
+
});
|
|
24395
|
+
}
|
|
24396
|
+
return findings;
|
|
24397
|
+
}
|
|
24246
24398
|
function analyzeMcpConfigs(files) {
|
|
24247
24399
|
const findings = [];
|
|
24248
24400
|
for (const file of files.filter((candidate) => isMcpConfigPath(candidate.path))) {
|
|
@@ -24336,7 +24488,10 @@ function calculateEvidenceScore(summary, findings) {
|
|
|
24336
24488
|
"sensitive-path",
|
|
24337
24489
|
"missing-tests",
|
|
24338
24490
|
"dependency-added",
|
|
24491
|
+
"dependency-major-upgrade",
|
|
24492
|
+
"dependency-lifecycle-script",
|
|
24339
24493
|
"workflow-permission-change",
|
|
24494
|
+
"workflow-dangerous-trigger",
|
|
24340
24495
|
"mcp-credential-risk"
|
|
24341
24496
|
].includes(finding.ruleId));
|
|
24342
24497
|
if (needsVerificationEvidence && !summary.verificationEvidence) {
|
|
@@ -24352,6 +24507,9 @@ function calculateEvidenceScore(summary, findings) {
|
|
|
24352
24507
|
else if (finding.ruleId === "workflow-permission-change") {
|
|
24353
24508
|
addDeduction("workflow-permission-change", 25, "Workflow permission changes need deliberate review.");
|
|
24354
24509
|
}
|
|
24510
|
+
else if (finding.ruleId === "workflow-dangerous-trigger") {
|
|
24511
|
+
addDeduction("workflow-dangerous-trigger", 30, "pull_request_target workflows need privileged trigger review.");
|
|
24512
|
+
}
|
|
24355
24513
|
else if (finding.ruleId === "mcp-credential-risk") {
|
|
24356
24514
|
addDeduction("mcp-credential-risk", 25, "MCP configuration expands local execution or credential risk.");
|
|
24357
24515
|
}
|
|
@@ -24366,6 +24524,12 @@ function calculateEvidenceScore(summary, findings) {
|
|
|
24366
24524
|
else if (finding.ruleId === "dependency-added") {
|
|
24367
24525
|
addDeduction("dependency-change", 10, "Dependency manifest changed.");
|
|
24368
24526
|
}
|
|
24527
|
+
else if (finding.ruleId === "dependency-major-upgrade") {
|
|
24528
|
+
addDeduction("dependency-major-upgrade", 15, "Dependency major version changed.");
|
|
24529
|
+
}
|
|
24530
|
+
else if (finding.ruleId === "dependency-lifecycle-script") {
|
|
24531
|
+
addDeduction("dependency-lifecycle-script", 25, "Package lifecycle scripts can run during install or publish.");
|
|
24532
|
+
}
|
|
24369
24533
|
else if (finding.ruleId === "missing-tests") {
|
|
24370
24534
|
addDeduction("missing-tests", finding.severity === "medium" ? 20 : 12, "Code changed without test changes or verification notes.");
|
|
24371
24535
|
}
|
|
@@ -24417,11 +24581,14 @@ function gradeEvidenceScore(value) {
|
|
|
24417
24581
|
function calculateReviewDecision(risk, evidenceScore, findings) {
|
|
24418
24582
|
const hasBlockingSecurityFinding = findings.some((finding) => finding.ruleId.startsWith("secret-detected") ||
|
|
24419
24583
|
finding.ruleId === "workflow-permission-change" ||
|
|
24584
|
+
finding.ruleId === "workflow-dangerous-trigger" ||
|
|
24585
|
+
finding.ruleId === "dependency-lifecycle-script" ||
|
|
24420
24586
|
finding.ruleId === "mcp-credential-risk");
|
|
24421
24587
|
if (hasBlockingSecurityFinding || evidenceScore.value < 50 || risk === "high") {
|
|
24422
24588
|
return "block-merge";
|
|
24423
24589
|
}
|
|
24424
|
-
if (evidenceScore.value < 70 ||
|
|
24590
|
+
if (evidenceScore.value < 70 ||
|
|
24591
|
+
findings.some((finding) => finding.ruleId === "missing-tests" || finding.ruleId === "thin-pr-description")) {
|
|
24425
24592
|
return "needs-evidence";
|
|
24426
24593
|
}
|
|
24427
24594
|
if (risk === "medium") {
|
|
@@ -24558,6 +24725,28 @@ function reviewActionsForFinding(finding) {
|
|
|
24558
24725
|
}
|
|
24559
24726
|
];
|
|
24560
24727
|
}
|
|
24728
|
+
if (finding.ruleId === "workflow-dangerous-trigger") {
|
|
24729
|
+
return [
|
|
24730
|
+
{
|
|
24731
|
+
actionId: "review-privileged-pr-trigger",
|
|
24732
|
+
title: "Review privileged pull_request_target usage.",
|
|
24733
|
+
detail: "Confirm the workflow does not execute untrusted PR code with write tokens, secrets, or repository permissions.",
|
|
24734
|
+
priority: "high",
|
|
24735
|
+
relatedRuleIds: [finding.ruleId]
|
|
24736
|
+
}
|
|
24737
|
+
];
|
|
24738
|
+
}
|
|
24739
|
+
if (finding.ruleId === "dependency-lifecycle-script") {
|
|
24740
|
+
return [
|
|
24741
|
+
{
|
|
24742
|
+
actionId: "review-package-lifecycle-script",
|
|
24743
|
+
title: "Review package lifecycle scripts before merge.",
|
|
24744
|
+
detail: "Check whether install, postinstall, prepare, or publish scripts can execute unexpected code for contributors or consumers.",
|
|
24745
|
+
priority: "high",
|
|
24746
|
+
relatedRuleIds: [finding.ruleId]
|
|
24747
|
+
}
|
|
24748
|
+
];
|
|
24749
|
+
}
|
|
24561
24750
|
if (finding.ruleId === "mcp-credential-risk") {
|
|
24562
24751
|
return [
|
|
24563
24752
|
{
|
|
@@ -24591,6 +24780,17 @@ function reviewActionsForFinding(finding) {
|
|
|
24591
24780
|
}
|
|
24592
24781
|
];
|
|
24593
24782
|
}
|
|
24783
|
+
if (finding.ruleId === "dependency-major-upgrade") {
|
|
24784
|
+
return [
|
|
24785
|
+
{
|
|
24786
|
+
actionId: "review-major-dependency-upgrade",
|
|
24787
|
+
title: "Review major dependency upgrade impact.",
|
|
24788
|
+
detail: "Check changelogs, migration notes, peer dependencies, and whether tests cover the upgraded surface.",
|
|
24789
|
+
priority: "medium",
|
|
24790
|
+
relatedRuleIds: [finding.ruleId]
|
|
24791
|
+
}
|
|
24792
|
+
];
|
|
24793
|
+
}
|
|
24594
24794
|
if (finding.ruleId === "sensitive-path") {
|
|
24595
24795
|
return [
|
|
24596
24796
|
{
|
|
@@ -24664,7 +24864,7 @@ const build_program = new Command();
|
|
|
24664
24864
|
build_program
|
|
24665
24865
|
.name("proof-pr")
|
|
24666
24866
|
.description("Review pull request evidence, scope, and safety before maintainers spend time on it.")
|
|
24667
|
-
.version("0.1.
|
|
24867
|
+
.version("0.1.6");
|
|
24668
24868
|
build_program
|
|
24669
24869
|
.command("scan", { isDefault: true })
|
|
24670
24870
|
.description("Scan a git diff and print a ProofPR report.")
|
|
@@ -24792,6 +24992,7 @@ comment:
|
|
|
24792
24992
|
# dependencies:
|
|
24793
24993
|
# flagNewPackages: true
|
|
24794
24994
|
# flagMajorUpgrades: true
|
|
24995
|
+
# flagLifecycleScripts: true
|
|
24795
24996
|
`;
|
|
24796
24997
|
}
|
|
24797
24998
|
function renderWorkflowTemplate(failOn) {
|
|
@@ -24810,7 +25011,7 @@ jobs:
|
|
|
24810
25011
|
runs-on: ubuntu-latest
|
|
24811
25012
|
steps:
|
|
24812
25013
|
- uses: actions/checkout@v4
|
|
24813
|
-
- uses: linsk27/proof-pr@v0.1.
|
|
25014
|
+
- uses: linsk27/proof-pr@v0.1.6
|
|
24814
25015
|
with:
|
|
24815
25016
|
fail-on: ${failOn}
|
|
24816
25017
|
comment: "true"
|
package/package.json
CHANGED
package/dist/index.d.ts
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|