patchdrill 0.1.0
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/.patchdrill.yml +33 -0
- package/CHANGELOG.md +150 -0
- package/CONTRIBUTING.md +59 -0
- package/LICENSE +21 -0
- package/README.md +601 -0
- package/SECURITY.md +28 -0
- package/action.yml +338 -0
- package/dist/baseline.d.ts +9 -0
- package/dist/baseline.js +38 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +662 -0
- package/dist/cli.js.map +1 -0
- package/dist/codeowners.d.ts +14 -0
- package/dist/codeowners.js +104 -0
- package/dist/codeowners.js.map +1 -0
- package/dist/command-plan.d.ts +3 -0
- package/dist/command-plan.js +26 -0
- package/dist/command-plan.js.map +1 -0
- package/dist/demo.d.ts +5 -0
- package/dist/demo.js +525 -0
- package/dist/demo.js.map +1 -0
- package/dist/dependency.d.ts +4 -0
- package/dist/dependency.js +1424 -0
- package/dist/dependency.js.map +1 -0
- package/dist/doctor.d.ts +26 -0
- package/dist/doctor.js +183 -0
- package/dist/doctor.js.map +1 -0
- package/dist/evidence.d.ts +64 -0
- package/dist/evidence.js +352 -0
- package/dist/evidence.js.map +1 -0
- package/dist/git.d.ts +16 -0
- package/dist/git.js +349 -0
- package/dist/git.js.map +1 -0
- package/dist/i18n-catalog.d.ts +8 -0
- package/dist/i18n-catalog.js +446 -0
- package/dist/i18n-catalog.js.map +1 -0
- package/dist/i18n.d.ts +20 -0
- package/dist/i18n.js +67 -0
- package/dist/i18n.js.map +1 -0
- package/dist/init.d.ts +13 -0
- package/dist/init.js +312 -0
- package/dist/init.js.map +1 -0
- package/dist/markdown-links.d.ts +18 -0
- package/dist/markdown-links.js +180 -0
- package/dist/markdown-links.js.map +1 -0
- package/dist/package-scripts.d.ts +3 -0
- package/dist/package-scripts.js +55 -0
- package/dist/package-scripts.js.map +1 -0
- package/dist/planner.d.ts +8 -0
- package/dist/planner.js +2351 -0
- package/dist/planner.js.map +1 -0
- package/dist/policy.d.ts +12 -0
- package/dist/policy.js +255 -0
- package/dist/policy.js.map +1 -0
- package/dist/project.d.ts +2 -0
- package/dist/project.js +1085 -0
- package/dist/project.js.map +1 -0
- package/dist/release-readiness.d.ts +25 -0
- package/dist/release-readiness.js +426 -0
- package/dist/release-readiness.js.map +1 -0
- package/dist/report-annotations.d.ts +3 -0
- package/dist/report-annotations.js +28 -0
- package/dist/report-annotations.js.map +1 -0
- package/dist/report-contract.d.ts +2 -0
- package/dist/report-contract.js +82 -0
- package/dist/report-contract.js.map +1 -0
- package/dist/report-html.d.ts +7 -0
- package/dist/report-html.js +706 -0
- package/dist/report-html.js.map +1 -0
- package/dist/report-sarif.d.ts +2 -0
- package/dist/report-sarif.js +90 -0
- package/dist/report-sarif.js.map +1 -0
- package/dist/report.d.ts +14 -0
- package/dist/report.js +310 -0
- package/dist/report.js.map +1 -0
- package/dist/risk.d.ts +19 -0
- package/dist/risk.js +1226 -0
- package/dist/risk.js.map +1 -0
- package/dist/runner.d.ts +8 -0
- package/dist/runner.js +113 -0
- package/dist/runner.js.map +1 -0
- package/dist/scan.d.ts +2 -0
- package/dist/scan.js +195 -0
- package/dist/scan.js.map +1 -0
- package/dist/schema.d.ts +12 -0
- package/dist/schema.js +30 -0
- package/dist/schema.js.map +1 -0
- package/dist/stack-coverage.d.ts +8 -0
- package/dist/stack-coverage.js +94 -0
- package/dist/stack-coverage.js.map +1 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verification.d.ts +11 -0
- package/dist/verification.js +108 -0
- package/dist/verification.js.map +1 -0
- package/docs/ANNOTATIONS.md +34 -0
- package/docs/ARCHITECTURE.md +79 -0
- package/docs/BASELINES.md +32 -0
- package/docs/CASE_STUDIES.md +106 -0
- package/docs/CODEOWNERS.md +23 -0
- package/docs/DASHBOARD.md +87 -0
- package/docs/EVIDENCE.md +55 -0
- package/docs/LAUNCH_PLAYBOOK.md +103 -0
- package/docs/MONOREPOS.md +74 -0
- package/docs/POLICY.md +98 -0
- package/docs/PROOF_PACKS.md +57 -0
- package/docs/PR_COMMENTS.md +56 -0
- package/docs/RELEASE.md +35 -0
- package/docs/ROADMAP.md +152 -0
- package/docs/RULE_CATALOG.md +90 -0
- package/docs/SARIF.md +74 -0
- package/docs/SCHEMAS.md +49 -0
- package/docs/SECURITY_POSTURE.md +32 -0
- package/docs/STACK_COVERAGE.md +20 -0
- package/docs/assets/patchdrill-demo.svg +21 -0
- package/docs/media/patchdrill-dashboard.png +0 -0
- package/docs/media/patchdrill-demo.gif +0 -0
- package/examples/case-studies/README.md +20 -0
- package/examples/demo/README.md +21 -0
- package/examples/demo/patchdrill-demo-summary.md +35 -0
- package/examples/demo/patchdrill-demo.html +623 -0
- package/examples/demo/patchdrill-demo.json +355 -0
- package/examples/demo/patchdrill-demo.md +120 -0
- package/examples/demo/patchdrill-demo.sarif +195 -0
- package/examples/report.md +128 -0
- package/examples/risky-agent-pr/README.md +15 -0
- package/examples/risky-agent-pr/patchdrill-demo-summary.md +41 -0
- package/examples/risky-agent-pr/patchdrill-demo.html +681 -0
- package/examples/risky-agent-pr/patchdrill-demo.json +483 -0
- package/examples/risky-agent-pr/patchdrill-demo.md +140 -0
- package/examples/risky-agent-pr/patchdrill-demo.sarif +398 -0
- package/fixtures/stacks/README.md +4 -0
- package/fixtures/stacks/android-gradle/fixture.json +33 -0
- package/fixtures/stacks/aspnet-core-service/fixture.json +36 -0
- package/fixtures/stacks/bazel-workspace/fixture.json +30 -0
- package/fixtures/stacks/buck2-workspace/fixture.json +30 -0
- package/fixtures/stacks/cargo-workspace/fixture.json +48 -0
- package/fixtures/stacks/django-app/fixture.json +25 -0
- package/fixtures/stacks/docker-compose/fixture.json +17 -0
- package/fixtures/stacks/dockerfile-service/fixture.json +17 -0
- package/fixtures/stacks/dotnet-service/fixture.json +36 -0
- package/fixtures/stacks/dotnet-solution-filter/fixture.json +62 -0
- package/fixtures/stacks/fastapi-app/fixture.json +29 -0
- package/fixtures/stacks/go-workspace/fixture.json +48 -0
- package/fixtures/stacks/java-gradle/fixture.json +29 -0
- package/fixtures/stacks/java-maven/fixture.json +32 -0
- package/fixtures/stacks/kubernetes-helm/fixture.json +25 -0
- package/fixtures/stacks/kubernetes-kustomize/fixture.json +21 -0
- package/fixtures/stacks/nested-go-workspace/fixture.json +51 -0
- package/fixtures/stacks/nextjs-app/fixture.json +34 -0
- package/fixtures/stacks/node-turbo-workspace/fixture.json +39 -0
- package/fixtures/stacks/pants-python/fixture.json +33 -0
- package/fixtures/stacks/php-composer/fixture.json +31 -0
- package/fixtures/stacks/python-service/fixture.json +21 -0
- package/fixtures/stacks/rails-app/fixture.json +25 -0
- package/fixtures/stacks/spring-boot-gradle/fixture.json +29 -0
- package/fixtures/stacks/spring-boot-maven/fixture.json +43 -0
- package/fixtures/stacks/swift-package/fixture.json +21 -0
- package/fixtures/stacks/terraform-module/fixture.json +17 -0
- package/fixtures/stacks/uv-python-service/fixture.json +47 -0
- package/fixtures/stacks/xcode-app/fixture.json +72 -0
- package/package.json +80 -0
- package/schemas/patchdrill-doctor.schema.json +171 -0
- package/schemas/patchdrill-evidence.schema.json +239 -0
- package/schemas/patchdrill-policy.schema.json +170 -0
- package/schemas/patchdrill-release-check.schema.json +78 -0
- package/schemas/patchdrill-report.schema.json +647 -0
package/action.yml
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
name: PatchDrill
|
|
2
|
+
description: Generate deterministic patch verification evidence for pull requests.
|
|
3
|
+
author: PatchDrill contributors
|
|
4
|
+
inputs:
|
|
5
|
+
base:
|
|
6
|
+
description: Base ref to compare against.
|
|
7
|
+
required: false
|
|
8
|
+
default: origin/main
|
|
9
|
+
fail-on:
|
|
10
|
+
description: Severity threshold that should fail the action.
|
|
11
|
+
required: false
|
|
12
|
+
default: high
|
|
13
|
+
max-risk:
|
|
14
|
+
description: Maximum allowed risk score from 0 to 100.
|
|
15
|
+
required: false
|
|
16
|
+
default: "69"
|
|
17
|
+
max-risk-delta:
|
|
18
|
+
description: Maximum allowed risk increase against the optional baseline report.
|
|
19
|
+
required: false
|
|
20
|
+
default: ""
|
|
21
|
+
max-output-chars:
|
|
22
|
+
description: Maximum characters to retain from each command output stream.
|
|
23
|
+
required: false
|
|
24
|
+
default: "20000"
|
|
25
|
+
command-timeout-ms:
|
|
26
|
+
description: Optional timeout in milliseconds for each verification command.
|
|
27
|
+
required: false
|
|
28
|
+
default: ""
|
|
29
|
+
config:
|
|
30
|
+
description: Optional PatchDrill policy path.
|
|
31
|
+
required: false
|
|
32
|
+
default: ""
|
|
33
|
+
baseline:
|
|
34
|
+
description: Optional previous PatchDrill JSON report to compare against.
|
|
35
|
+
required: false
|
|
36
|
+
default: ""
|
|
37
|
+
run:
|
|
38
|
+
description: Execute required verification commands.
|
|
39
|
+
required: false
|
|
40
|
+
default: "false"
|
|
41
|
+
run-optional:
|
|
42
|
+
description: Execute optional inferred verification commands when run is true.
|
|
43
|
+
required: false
|
|
44
|
+
default: "false"
|
|
45
|
+
evidence:
|
|
46
|
+
description: Audit evidence manifest output path.
|
|
47
|
+
required: false
|
|
48
|
+
default: patchdrill-evidence.json
|
|
49
|
+
summary:
|
|
50
|
+
description: Compact Markdown summary output path for PR comments and step summaries.
|
|
51
|
+
required: false
|
|
52
|
+
default: patchdrill-summary.md
|
|
53
|
+
markdown:
|
|
54
|
+
description: Markdown report output path.
|
|
55
|
+
required: false
|
|
56
|
+
default: patchdrill-report.md
|
|
57
|
+
json:
|
|
58
|
+
description: JSON report output path.
|
|
59
|
+
required: false
|
|
60
|
+
default: patchdrill-report.json
|
|
61
|
+
sarif:
|
|
62
|
+
description: SARIF report output path.
|
|
63
|
+
required: false
|
|
64
|
+
default: patchdrill.sarif
|
|
65
|
+
html:
|
|
66
|
+
description: Static HTML dashboard output path.
|
|
67
|
+
required: false
|
|
68
|
+
default: patchdrill-dashboard.html
|
|
69
|
+
dashboard-history:
|
|
70
|
+
description: Newline-separated previous PatchDrill JSON report paths to include in the static HTML dashboard trend.
|
|
71
|
+
required: false
|
|
72
|
+
default: ""
|
|
73
|
+
annotations:
|
|
74
|
+
description: Emit GitHub Actions annotations for PatchDrill findings.
|
|
75
|
+
required: false
|
|
76
|
+
default: "true"
|
|
77
|
+
step-summary:
|
|
78
|
+
description: Append the compact Markdown summary to the GitHub Actions step summary.
|
|
79
|
+
required: false
|
|
80
|
+
default: "true"
|
|
81
|
+
pr-comment:
|
|
82
|
+
description: Upsert the compact Markdown summary as a pull request comment.
|
|
83
|
+
required: false
|
|
84
|
+
default: "false"
|
|
85
|
+
comment-marker:
|
|
86
|
+
description: Hidden marker used to find and update an existing PatchDrill PR comment.
|
|
87
|
+
required: false
|
|
88
|
+
default: "<!-- patchdrill-report -->"
|
|
89
|
+
outputs:
|
|
90
|
+
report-evidence:
|
|
91
|
+
description: Path to the generated audit evidence manifest.
|
|
92
|
+
value: ${{ steps.paths.outputs.evidence }}
|
|
93
|
+
report-summary:
|
|
94
|
+
description: Path to the generated compact Markdown summary.
|
|
95
|
+
value: ${{ steps.paths.outputs.summary }}
|
|
96
|
+
report-json:
|
|
97
|
+
description: Path to the generated JSON report.
|
|
98
|
+
value: ${{ steps.paths.outputs.json }}
|
|
99
|
+
report-markdown:
|
|
100
|
+
description: Path to the generated Markdown report.
|
|
101
|
+
value: ${{ steps.paths.outputs.markdown }}
|
|
102
|
+
report-sarif:
|
|
103
|
+
description: Path to the generated SARIF report.
|
|
104
|
+
value: ${{ steps.paths.outputs.sarif }}
|
|
105
|
+
report-html:
|
|
106
|
+
description: Path to the generated HTML dashboard.
|
|
107
|
+
value: ${{ steps.paths.outputs.html }}
|
|
108
|
+
runs:
|
|
109
|
+
using: composite
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/setup-node@v6
|
|
112
|
+
with:
|
|
113
|
+
node-version: 22
|
|
114
|
+
cache: npm
|
|
115
|
+
cache-dependency-path: ${{ github.action_path }}/package-lock.json
|
|
116
|
+
- name: Install PatchDrill action dependencies
|
|
117
|
+
shell: bash
|
|
118
|
+
working-directory: ${{ github.action_path }}
|
|
119
|
+
run: npm ci --ignore-scripts
|
|
120
|
+
- name: Build PatchDrill action
|
|
121
|
+
shell: bash
|
|
122
|
+
working-directory: ${{ github.action_path }}
|
|
123
|
+
run: npm run build
|
|
124
|
+
- name: Run PatchDrill
|
|
125
|
+
id: run
|
|
126
|
+
shell: bash
|
|
127
|
+
env:
|
|
128
|
+
PATCHDRILL_BASE: ${{ inputs.base }}
|
|
129
|
+
PATCHDRILL_FAIL_ON: ${{ inputs.fail-on }}
|
|
130
|
+
PATCHDRILL_MAX_RISK: ${{ inputs.max-risk }}
|
|
131
|
+
PATCHDRILL_MAX_RISK_DELTA: ${{ inputs.max-risk-delta }}
|
|
132
|
+
PATCHDRILL_MAX_OUTPUT_CHARS: ${{ inputs.max-output-chars }}
|
|
133
|
+
PATCHDRILL_COMMAND_TIMEOUT_MS: ${{ inputs.command-timeout-ms }}
|
|
134
|
+
PATCHDRILL_CONFIG: ${{ inputs.config }}
|
|
135
|
+
PATCHDRILL_BASELINE: ${{ inputs.baseline }}
|
|
136
|
+
PATCHDRILL_RUN: ${{ inputs.run }}
|
|
137
|
+
PATCHDRILL_RUN_OPTIONAL: ${{ inputs.run-optional }}
|
|
138
|
+
PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
|
|
139
|
+
PATCHDRILL_SUMMARY: ${{ inputs.summary }}
|
|
140
|
+
PATCHDRILL_MARKDOWN: ${{ inputs.markdown }}
|
|
141
|
+
PATCHDRILL_JSON: ${{ inputs.json }}
|
|
142
|
+
PATCHDRILL_SARIF: ${{ inputs.sarif }}
|
|
143
|
+
PATCHDRILL_HTML: ${{ inputs.html }}
|
|
144
|
+
PATCHDRILL_ANNOTATIONS: ${{ inputs.annotations }}
|
|
145
|
+
run: |
|
|
146
|
+
args=(
|
|
147
|
+
scan
|
|
148
|
+
--base "$PATCHDRILL_BASE"
|
|
149
|
+
--evidence "$PATCHDRILL_EVIDENCE"
|
|
150
|
+
--summary-markdown "$PATCHDRILL_SUMMARY"
|
|
151
|
+
--markdown "$PATCHDRILL_MARKDOWN"
|
|
152
|
+
--json "$PATCHDRILL_JSON"
|
|
153
|
+
--sarif "$PATCHDRILL_SARIF"
|
|
154
|
+
--html "$PATCHDRILL_HTML"
|
|
155
|
+
--fail-on "$PATCHDRILL_FAIL_ON"
|
|
156
|
+
--max-risk "$PATCHDRILL_MAX_RISK"
|
|
157
|
+
--max-output-chars "$PATCHDRILL_MAX_OUTPUT_CHARS"
|
|
158
|
+
--run "${PATCHDRILL_RUN:-false}"
|
|
159
|
+
--run-optional "${PATCHDRILL_RUN_OPTIONAL:-false}"
|
|
160
|
+
--github-annotations "${PATCHDRILL_ANNOTATIONS:-false}"
|
|
161
|
+
)
|
|
162
|
+
if [ -n "$PATCHDRILL_CONFIG" ]; then
|
|
163
|
+
args+=(--config "$PATCHDRILL_CONFIG")
|
|
164
|
+
fi
|
|
165
|
+
if [ -n "$PATCHDRILL_BASELINE" ]; then
|
|
166
|
+
args+=(--baseline "$PATCHDRILL_BASELINE")
|
|
167
|
+
fi
|
|
168
|
+
if [ -n "$PATCHDRILL_MAX_RISK_DELTA" ]; then
|
|
169
|
+
args+=(--max-risk-delta "$PATCHDRILL_MAX_RISK_DELTA")
|
|
170
|
+
fi
|
|
171
|
+
if [ -n "$PATCHDRILL_COMMAND_TIMEOUT_MS" ]; then
|
|
172
|
+
args+=(--command-timeout-ms "$PATCHDRILL_COMMAND_TIMEOUT_MS")
|
|
173
|
+
fi
|
|
174
|
+
set +e
|
|
175
|
+
node "$GITHUB_ACTION_PATH/dist/cli.js" "${args[@]}"
|
|
176
|
+
STATUS=$?
|
|
177
|
+
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
|
|
178
|
+
exit 0
|
|
179
|
+
- name: Render dashboard history
|
|
180
|
+
if: inputs.dashboard-history != ''
|
|
181
|
+
shell: bash
|
|
182
|
+
env:
|
|
183
|
+
PATCHDRILL_DASHBOARD_HISTORY: ${{ inputs.dashboard-history }}
|
|
184
|
+
PATCHDRILL_JSON: ${{ inputs.json }}
|
|
185
|
+
PATCHDRILL_HTML: ${{ inputs.html }}
|
|
186
|
+
run: |
|
|
187
|
+
args=(dashboard)
|
|
188
|
+
while IFS= read -r report_path; do
|
|
189
|
+
if [ -n "$report_path" ]; then
|
|
190
|
+
args+=(--json "$report_path")
|
|
191
|
+
fi
|
|
192
|
+
done <<< "$PATCHDRILL_DASHBOARD_HISTORY"
|
|
193
|
+
args+=(--json "$PATCHDRILL_JSON" --output "$PATCHDRILL_HTML")
|
|
194
|
+
node "$GITHUB_ACTION_PATH/dist/cli.js" "${args[@]}"
|
|
195
|
+
- name: Refresh evidence manifest
|
|
196
|
+
shell: bash
|
|
197
|
+
env:
|
|
198
|
+
PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
|
|
199
|
+
PATCHDRILL_SUMMARY: ${{ inputs.summary }}
|
|
200
|
+
PATCHDRILL_MARKDOWN: ${{ inputs.markdown }}
|
|
201
|
+
PATCHDRILL_JSON: ${{ inputs.json }}
|
|
202
|
+
PATCHDRILL_SARIF: ${{ inputs.sarif }}
|
|
203
|
+
PATCHDRILL_HTML: ${{ inputs.html }}
|
|
204
|
+
run: |
|
|
205
|
+
if [ -n "$PATCHDRILL_EVIDENCE" ] && [ -n "$PATCHDRILL_JSON" ]; then
|
|
206
|
+
args=(evidence --json "$PATCHDRILL_JSON" --evidence "$PATCHDRILL_EVIDENCE")
|
|
207
|
+
if [ -n "$PATCHDRILL_SUMMARY" ]; then
|
|
208
|
+
args+=(--summary-markdown "$PATCHDRILL_SUMMARY")
|
|
209
|
+
fi
|
|
210
|
+
if [ -n "$PATCHDRILL_MARKDOWN" ]; then
|
|
211
|
+
args+=(--markdown "$PATCHDRILL_MARKDOWN")
|
|
212
|
+
fi
|
|
213
|
+
if [ -n "$PATCHDRILL_SARIF" ]; then
|
|
214
|
+
args+=(--sarif "$PATCHDRILL_SARIF")
|
|
215
|
+
fi
|
|
216
|
+
if [ -n "$PATCHDRILL_HTML" ]; then
|
|
217
|
+
args+=(--html "$PATCHDRILL_HTML")
|
|
218
|
+
fi
|
|
219
|
+
node "$GITHUB_ACTION_PATH/dist/cli.js" "${args[@]}"
|
|
220
|
+
fi
|
|
221
|
+
- name: Verify evidence manifest
|
|
222
|
+
shell: bash
|
|
223
|
+
env:
|
|
224
|
+
PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
|
|
225
|
+
run: |
|
|
226
|
+
if [ -n "$PATCHDRILL_EVIDENCE" ]; then
|
|
227
|
+
node "$GITHUB_ACTION_PATH/dist/cli.js" verify --evidence "$PATCHDRILL_EVIDENCE"
|
|
228
|
+
fi
|
|
229
|
+
- name: Write step summary
|
|
230
|
+
shell: bash
|
|
231
|
+
env:
|
|
232
|
+
PATCHDRILL_STEP_SUMMARY: ${{ inputs.step-summary }}
|
|
233
|
+
PATCHDRILL_SUMMARY: ${{ inputs.summary }}
|
|
234
|
+
run: |
|
|
235
|
+
bool_true() {
|
|
236
|
+
local value="${1,,}"
|
|
237
|
+
case "$value" in
|
|
238
|
+
true|1|yes|on) return 0 ;;
|
|
239
|
+
false|0|no|off|"") return 1 ;;
|
|
240
|
+
*)
|
|
241
|
+
echo "Invalid boolean input for step-summary: $1" >&2
|
|
242
|
+
exit 1
|
|
243
|
+
;;
|
|
244
|
+
esac
|
|
245
|
+
}
|
|
246
|
+
if ! bool_true "$PATCHDRILL_STEP_SUMMARY"; then
|
|
247
|
+
exit 0
|
|
248
|
+
fi
|
|
249
|
+
if [ -n "${GITHUB_STEP_SUMMARY:-}" ] && [ -f "$PATCHDRILL_SUMMARY" ]; then
|
|
250
|
+
cat -- "$PATCHDRILL_SUMMARY" >> "$GITHUB_STEP_SUMMARY"
|
|
251
|
+
fi
|
|
252
|
+
- name: Upsert PR comment
|
|
253
|
+
if: github.event_name == 'pull_request'
|
|
254
|
+
uses: actions/github-script@v9
|
|
255
|
+
env:
|
|
256
|
+
PATCHDRILL_PR_COMMENT: ${{ inputs.pr-comment }}
|
|
257
|
+
PATCHDRILL_SUMMARY: ${{ inputs.summary }}
|
|
258
|
+
PATCHDRILL_MARKER: ${{ inputs.comment-marker }}
|
|
259
|
+
with:
|
|
260
|
+
script: |
|
|
261
|
+
const fs = require("fs");
|
|
262
|
+
const boolTrue = (name) => {
|
|
263
|
+
const raw = process.env[name] || "";
|
|
264
|
+
const value = raw.trim().toLowerCase();
|
|
265
|
+
if (["true", "1", "yes", "on"].includes(value)) return true;
|
|
266
|
+
if (["false", "0", "no", "off", ""].includes(value)) return false;
|
|
267
|
+
throw new Error(`Invalid boolean input for pr-comment: ${raw}`);
|
|
268
|
+
};
|
|
269
|
+
if (!boolTrue("PATCHDRILL_PR_COMMENT")) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const marker = process.env.PATCHDRILL_MARKER || "<!-- patchdrill-report -->";
|
|
273
|
+
const summaryPath = process.env.PATCHDRILL_SUMMARY || "patchdrill-summary.md";
|
|
274
|
+
if (!fs.existsSync(summaryPath)) {
|
|
275
|
+
core.warning(`PatchDrill summary report not found: ${summaryPath}`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const body = `${marker}\n${fs.readFileSync(summaryPath, "utf8")}`;
|
|
279
|
+
const { owner, repo } = context.repo;
|
|
280
|
+
const issue_number = context.payload.pull_request.number;
|
|
281
|
+
try {
|
|
282
|
+
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
283
|
+
owner,
|
|
284
|
+
repo,
|
|
285
|
+
issue_number,
|
|
286
|
+
per_page: 100
|
|
287
|
+
});
|
|
288
|
+
const existing = comments.find((comment) => comment.user?.type === "Bot" && comment.body?.includes(marker));
|
|
289
|
+
if (existing) {
|
|
290
|
+
await github.rest.issues.updateComment({
|
|
291
|
+
owner,
|
|
292
|
+
repo,
|
|
293
|
+
comment_id: existing.id,
|
|
294
|
+
body
|
|
295
|
+
});
|
|
296
|
+
} else {
|
|
297
|
+
await github.rest.issues.createComment({
|
|
298
|
+
owner,
|
|
299
|
+
repo,
|
|
300
|
+
issue_number,
|
|
301
|
+
body
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
306
|
+
core.warning(`PatchDrill PR comment skipped: ${message}`);
|
|
307
|
+
}
|
|
308
|
+
- name: Export report paths
|
|
309
|
+
id: paths
|
|
310
|
+
shell: bash
|
|
311
|
+
env:
|
|
312
|
+
PATCHDRILL_EVIDENCE: ${{ inputs.evidence }}
|
|
313
|
+
PATCHDRILL_SUMMARY: ${{ inputs.summary }}
|
|
314
|
+
PATCHDRILL_MARKDOWN: ${{ inputs.markdown }}
|
|
315
|
+
PATCHDRILL_JSON: ${{ inputs.json }}
|
|
316
|
+
PATCHDRILL_SARIF: ${{ inputs.sarif }}
|
|
317
|
+
PATCHDRILL_HTML: ${{ inputs.html }}
|
|
318
|
+
run: |
|
|
319
|
+
write_output() {
|
|
320
|
+
local name="$1"
|
|
321
|
+
local value="$2"
|
|
322
|
+
local delimiter="patchdrill_${name}_EOF"
|
|
323
|
+
{
|
|
324
|
+
printf '%s<<%s\n' "$name" "$delimiter"
|
|
325
|
+
printf '%s\n' "$value"
|
|
326
|
+
printf '%s\n' "$delimiter"
|
|
327
|
+
} >> "$GITHUB_OUTPUT"
|
|
328
|
+
}
|
|
329
|
+
write_output evidence "$PATCHDRILL_EVIDENCE"
|
|
330
|
+
write_output summary "$PATCHDRILL_SUMMARY"
|
|
331
|
+
write_output json "$PATCHDRILL_JSON"
|
|
332
|
+
write_output markdown "$PATCHDRILL_MARKDOWN"
|
|
333
|
+
write_output sarif "$PATCHDRILL_SARIF"
|
|
334
|
+
write_output html "$PATCHDRILL_HTML"
|
|
335
|
+
- name: Fail on PatchDrill gate
|
|
336
|
+
if: steps.run.outputs.status != '0'
|
|
337
|
+
shell: bash
|
|
338
|
+
run: exit "${{ steps.run.outputs.status }}"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PatchReport, PatchStatus, RiskFinding } from "./types.js";
|
|
2
|
+
export interface BaselineComparisonInput {
|
|
3
|
+
summary: {
|
|
4
|
+
status: PatchStatus;
|
|
5
|
+
riskScore: number;
|
|
6
|
+
};
|
|
7
|
+
findings: RiskFinding[];
|
|
8
|
+
}
|
|
9
|
+
export declare function compareBaseline(root: string, baselinePath: string, current: BaselineComparisonInput): NonNullable<PatchReport["baseline"]>;
|
package/dist/baseline.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
export function compareBaseline(root, baselinePath, current) {
|
|
4
|
+
const resolved = isAbsolute(baselinePath) ? baselinePath : resolve(root, baselinePath);
|
|
5
|
+
if (!existsSync(resolved))
|
|
6
|
+
throw new Error(`PatchDrill baseline report not found: ${resolved}`);
|
|
7
|
+
const baseline = readBaselineReport(resolved);
|
|
8
|
+
const previousFindings = new Set((baseline.findings ?? []).map(findingFingerprint));
|
|
9
|
+
const currentFindings = new Set(current.findings.map(findingFingerprint));
|
|
10
|
+
const newFindingCount = [...currentFindings].filter((fingerprint) => !previousFindings.has(fingerprint)).length;
|
|
11
|
+
const resolvedFindingCount = [...previousFindings].filter((fingerprint) => !currentFindings.has(fingerprint)).length;
|
|
12
|
+
const unchangedFindingCount = [...currentFindings].filter((fingerprint) => previousFindings.has(fingerprint)).length;
|
|
13
|
+
return {
|
|
14
|
+
path: relative(root, resolved),
|
|
15
|
+
...(baseline.summary?.status !== undefined ? { previousStatus: baseline.summary.status } : {}),
|
|
16
|
+
currentStatus: current.summary.status,
|
|
17
|
+
...(baseline.summary?.riskScore !== undefined ? { previousRiskScore: baseline.summary.riskScore } : {}),
|
|
18
|
+
currentRiskScore: current.summary.riskScore,
|
|
19
|
+
riskDelta: current.summary.riskScore - (baseline.summary?.riskScore ?? 0),
|
|
20
|
+
newFindingCount,
|
|
21
|
+
resolvedFindingCount,
|
|
22
|
+
unchangedFindingCount
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function readBaselineReport(path) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw new Error(`Failed to read PatchDrill baseline report ${path}: ${error instanceof Error ? error.message : String(error)}`, {
|
|
31
|
+
cause: error
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function findingFingerprint(finding) {
|
|
36
|
+
return [finding.ruleId ?? "", finding.severity, finding.title, finding.file ?? "", finding.line ?? ""].join("\0");
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAW1D,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,YAAoB,EAAE,OAAgC;IAClG,MAAM,QAAQ,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACvF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,EAAE,CAAC,CAAC;IAChG,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACpF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC1E,MAAM,eAAe,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;IAChH,MAAM,oBAAoB,GAAG,CAAC,GAAG,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;IACrH,MAAM,qBAAqB,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;IAErH,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;QAC9B,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM;QACrC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvG,gBAAgB,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS;QAC3C,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,IAAI,CAAC,CAAC;QACzE,eAAe;QACf,oBAAoB;QACpB,qBAAqB;KACtB,CAAC;AACJ,CAAC;AASD,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAmB,CAAC;IAClE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,6CAA6C,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE;YAC9H,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAoB;IAC9C,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpH,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type Locale } from "./i18n.js";
|
|
3
|
+
import { type GateOptions } from "./report.js";
|
|
4
|
+
import { scan } from "./scan.js";
|
|
5
|
+
export interface ParsedArgs {
|
|
6
|
+
command: string;
|
|
7
|
+
flags: Record<string, string | boolean | string[]>;
|
|
8
|
+
positionals: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function scanCommand(parsed: ParsedArgs): Promise<void>;
|
|
11
|
+
export declare function dashboardCommand(parsed: ParsedArgs): void;
|
|
12
|
+
export declare function demoCommand(parsed: ParsedArgs): void;
|
|
13
|
+
export declare function doctorCommand(parsed?: ParsedArgs): void;
|
|
14
|
+
export declare function evidenceCommand(parsed: ParsedArgs): void;
|
|
15
|
+
export declare function explainCommand(): void;
|
|
16
|
+
export declare function renderExplainText(): string;
|
|
17
|
+
export declare function releaseCheckCommand(parsed?: ParsedArgs): void;
|
|
18
|
+
export declare function renderConsoleSummary(report: Awaited<ReturnType<typeof scan>>, gateOptions: GateOptions, locale?: Locale): string;
|
|
19
|
+
export declare function parseArgs(args: string[]): ParsedArgs;
|