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 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
- const addedDependencyLines = file.addedLines.filter((line) => isDependencyLikeAddition(file.path, line.value.trim()));
24189
- if (addedDependencyLines.length === 0) {
24190
- continue;
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*"(?<value>[^"]*)"/.exec(line);
24312
+ const match = /^"(?<key>[@A-Za-z0-9_.-]+)"\s*:\s*"(?<version>[^"]*)"/.exec(value);
24207
24313
  if (!match?.groups) {
24208
- return false;
24314
+ return undefined;
24209
24315
  }
24210
- const { key, value } = match.groups;
24211
- if (!key || !value || PACKAGE_JSON_NON_DEPENDENCY_KEYS.has(key)) {
24212
- return false;
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 /^(?:\^|~|>=?|<=?|\d|workspace:|npm:|file:|link:|portal:|git\+|https?:|github:)/.test(value);
24323
+ return { name: key, version, line };
24215
24324
  }
24216
24325
  if (path.endsWith("requirements.txt")) {
24217
- return /^[A-Za-z0-9_.-]+(?:\[.*\])?\s*(?:==|>=|<=|~=|>|<)\s*[^#\s]+/.test(line);
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
- return /^[A-Za-z0-9_.-]+\s*=\s*"(?:\^|~|>=?|<=?|\d|workspace:|path\s*=|git\s*=)[^"]*"/.test(line);
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
- return /^(?:require\s+)?[A-Za-z0-9_.\-/]+\s+v\d+\.\d+\.\d+/.test(line);
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 false;
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 || findings.some((finding) => finding.ruleId === "missing-tests" || finding.ruleId === "thin-pr-description")) {
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.5");
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.5
25014
+ - uses: linsk27/proof-pr@v0.1.6
24814
25015
  with:
24815
25016
  fail-on: ${failOn}
24816
25017
  comment: "true"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-pr",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "CLI for ProofPR, a maintainer-focused pull request evidence scanner.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EAET,MAAM,gBAAgB,CAAC;AAExB,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAwB1C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,sFAAsF,CAAC;KACnG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KACpC,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,cAAc,EAAE,yDAAyD,CAAC;KACjF,MAAM,CAAC,cAAc,EAAE,gCAAgC,EAAE,MAAM,CAAC;KAChE,MAAM,CAAC,oBAAoB,EAAE,8DAA8D,CAAC;KAC5F,MAAM,CAAC,oBAAoB,EAAE,8CAA8C,CAAC;KAC5E,MAAM,CAAC,kBAAkB,EAAE,6CAA6C,CAAC;KACzE,MAAM,CAAC,uBAAuB,EAAE,gDAAgD,CAAC;KACjF,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,cAAc,CAAC;KAClE,MAAM,CAAC,mBAAmB,EAAE,0CAA0C,EAAE,WAAW,EAAE,UAAU,CAAC;KAChG,MAAM,CAAC,mBAAmB,EAAE,8DAA8D,EAAE,cAAc,EAAE,OAAO,CAAC;KACpH,MAAM,CAAC,KAAK,EAAE,OAA2B,EAAE,EAAE;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ;QAC/B,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1C,CAAC,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,WAAW,GACf,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;QACnD,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE;QAC1C,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC;IAEpC,IAAI,kBAAkB,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4DAA4D,CAAC;KACzE,MAAM,CAAC,sBAAsB,EAAE,+CAA+C,EAAE,cAAc,CAAC;KAC/F,MAAM,CACL,wBAAwB,EACxB,4CAA4C,EAC5C,+BAA+B,CAChC;KACA,MAAM,CAAC,mBAAmB,EAAE,0DAA0D,EAAE,cAAc,EAAE,MAAM,CAAC;KAC/G,MAAM,CAAC,SAAS,EAAE,2BAA2B,EAAE,KAAK,CAAC;KACrD,MAAM,CAAC,KAAK,EAAE,OAA2B,EAAE,EAAE;IAC5C,MAAM,cAAc,CAAC,OAAO,CAAC,UAAU,EAAE,oBAAoB,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAChF,MAAM,cAAc,CAAC,OAAO,CAAC,YAAY,EAAE,sBAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAClG,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2BAA2B,OAAO,CAAC,UAAU,OAAO,OAAO,CAAC,YAAY,IAAI,CAC7E,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACxD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,OAAO,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,WAAW,CAAC,IAAwB,EAAE,IAAY;IAC/D,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;IAEtD,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;IACrF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,OAA2B;IAC5D,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAc;IAC1E,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,gDAAgD,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCR,CAAC;AACF,CAAC;AAED,SAAS,sBAAsB,CAAC,MAAiB;IAC/C,OAAO;;;;;;;;;;;;;;;;;qBAiBY,MAAM;;CAE1B,CAAC;AACF,CAAC;AAED,SAAS,YAAY,CAAC,MAAmC,EAAE,MAAoB;IAC7E,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QAClE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,oBAAoB,CAAC,8CAA8C,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACnF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,oBAAoB,CAAC,kDAAkD,CAAC,CAAC;AACrF,CAAC"}