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.
Files changed (169) hide show
  1. package/.patchdrill.yml +33 -0
  2. package/CHANGELOG.md +150 -0
  3. package/CONTRIBUTING.md +59 -0
  4. package/LICENSE +21 -0
  5. package/README.md +601 -0
  6. package/SECURITY.md +28 -0
  7. package/action.yml +338 -0
  8. package/dist/baseline.d.ts +9 -0
  9. package/dist/baseline.js +38 -0
  10. package/dist/baseline.js.map +1 -0
  11. package/dist/cli.d.ts +19 -0
  12. package/dist/cli.js +662 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/codeowners.d.ts +14 -0
  15. package/dist/codeowners.js +104 -0
  16. package/dist/codeowners.js.map +1 -0
  17. package/dist/command-plan.d.ts +3 -0
  18. package/dist/command-plan.js +26 -0
  19. package/dist/command-plan.js.map +1 -0
  20. package/dist/demo.d.ts +5 -0
  21. package/dist/demo.js +525 -0
  22. package/dist/demo.js.map +1 -0
  23. package/dist/dependency.d.ts +4 -0
  24. package/dist/dependency.js +1424 -0
  25. package/dist/dependency.js.map +1 -0
  26. package/dist/doctor.d.ts +26 -0
  27. package/dist/doctor.js +183 -0
  28. package/dist/doctor.js.map +1 -0
  29. package/dist/evidence.d.ts +64 -0
  30. package/dist/evidence.js +352 -0
  31. package/dist/evidence.js.map +1 -0
  32. package/dist/git.d.ts +16 -0
  33. package/dist/git.js +349 -0
  34. package/dist/git.js.map +1 -0
  35. package/dist/i18n-catalog.d.ts +8 -0
  36. package/dist/i18n-catalog.js +446 -0
  37. package/dist/i18n-catalog.js.map +1 -0
  38. package/dist/i18n.d.ts +20 -0
  39. package/dist/i18n.js +67 -0
  40. package/dist/i18n.js.map +1 -0
  41. package/dist/init.d.ts +13 -0
  42. package/dist/init.js +312 -0
  43. package/dist/init.js.map +1 -0
  44. package/dist/markdown-links.d.ts +18 -0
  45. package/dist/markdown-links.js +180 -0
  46. package/dist/markdown-links.js.map +1 -0
  47. package/dist/package-scripts.d.ts +3 -0
  48. package/dist/package-scripts.js +55 -0
  49. package/dist/package-scripts.js.map +1 -0
  50. package/dist/planner.d.ts +8 -0
  51. package/dist/planner.js +2351 -0
  52. package/dist/planner.js.map +1 -0
  53. package/dist/policy.d.ts +12 -0
  54. package/dist/policy.js +255 -0
  55. package/dist/policy.js.map +1 -0
  56. package/dist/project.d.ts +2 -0
  57. package/dist/project.js +1085 -0
  58. package/dist/project.js.map +1 -0
  59. package/dist/release-readiness.d.ts +25 -0
  60. package/dist/release-readiness.js +426 -0
  61. package/dist/release-readiness.js.map +1 -0
  62. package/dist/report-annotations.d.ts +3 -0
  63. package/dist/report-annotations.js +28 -0
  64. package/dist/report-annotations.js.map +1 -0
  65. package/dist/report-contract.d.ts +2 -0
  66. package/dist/report-contract.js +82 -0
  67. package/dist/report-contract.js.map +1 -0
  68. package/dist/report-html.d.ts +7 -0
  69. package/dist/report-html.js +706 -0
  70. package/dist/report-html.js.map +1 -0
  71. package/dist/report-sarif.d.ts +2 -0
  72. package/dist/report-sarif.js +90 -0
  73. package/dist/report-sarif.js.map +1 -0
  74. package/dist/report.d.ts +14 -0
  75. package/dist/report.js +310 -0
  76. package/dist/report.js.map +1 -0
  77. package/dist/risk.d.ts +19 -0
  78. package/dist/risk.js +1226 -0
  79. package/dist/risk.js.map +1 -0
  80. package/dist/runner.d.ts +8 -0
  81. package/dist/runner.js +113 -0
  82. package/dist/runner.js.map +1 -0
  83. package/dist/scan.d.ts +2 -0
  84. package/dist/scan.js +195 -0
  85. package/dist/scan.js.map +1 -0
  86. package/dist/schema.d.ts +12 -0
  87. package/dist/schema.js +30 -0
  88. package/dist/schema.js.map +1 -0
  89. package/dist/stack-coverage.d.ts +8 -0
  90. package/dist/stack-coverage.js +94 -0
  91. package/dist/stack-coverage.js.map +1 -0
  92. package/dist/types.d.ts +206 -0
  93. package/dist/types.js +2 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/verification.d.ts +11 -0
  96. package/dist/verification.js +108 -0
  97. package/dist/verification.js.map +1 -0
  98. package/docs/ANNOTATIONS.md +34 -0
  99. package/docs/ARCHITECTURE.md +79 -0
  100. package/docs/BASELINES.md +32 -0
  101. package/docs/CASE_STUDIES.md +106 -0
  102. package/docs/CODEOWNERS.md +23 -0
  103. package/docs/DASHBOARD.md +87 -0
  104. package/docs/EVIDENCE.md +55 -0
  105. package/docs/LAUNCH_PLAYBOOK.md +103 -0
  106. package/docs/MONOREPOS.md +74 -0
  107. package/docs/POLICY.md +98 -0
  108. package/docs/PROOF_PACKS.md +57 -0
  109. package/docs/PR_COMMENTS.md +56 -0
  110. package/docs/RELEASE.md +35 -0
  111. package/docs/ROADMAP.md +152 -0
  112. package/docs/RULE_CATALOG.md +90 -0
  113. package/docs/SARIF.md +74 -0
  114. package/docs/SCHEMAS.md +49 -0
  115. package/docs/SECURITY_POSTURE.md +32 -0
  116. package/docs/STACK_COVERAGE.md +20 -0
  117. package/docs/assets/patchdrill-demo.svg +21 -0
  118. package/docs/media/patchdrill-dashboard.png +0 -0
  119. package/docs/media/patchdrill-demo.gif +0 -0
  120. package/examples/case-studies/README.md +20 -0
  121. package/examples/demo/README.md +21 -0
  122. package/examples/demo/patchdrill-demo-summary.md +35 -0
  123. package/examples/demo/patchdrill-demo.html +623 -0
  124. package/examples/demo/patchdrill-demo.json +355 -0
  125. package/examples/demo/patchdrill-demo.md +120 -0
  126. package/examples/demo/patchdrill-demo.sarif +195 -0
  127. package/examples/report.md +128 -0
  128. package/examples/risky-agent-pr/README.md +15 -0
  129. package/examples/risky-agent-pr/patchdrill-demo-summary.md +41 -0
  130. package/examples/risky-agent-pr/patchdrill-demo.html +681 -0
  131. package/examples/risky-agent-pr/patchdrill-demo.json +483 -0
  132. package/examples/risky-agent-pr/patchdrill-demo.md +140 -0
  133. package/examples/risky-agent-pr/patchdrill-demo.sarif +398 -0
  134. package/fixtures/stacks/README.md +4 -0
  135. package/fixtures/stacks/android-gradle/fixture.json +33 -0
  136. package/fixtures/stacks/aspnet-core-service/fixture.json +36 -0
  137. package/fixtures/stacks/bazel-workspace/fixture.json +30 -0
  138. package/fixtures/stacks/buck2-workspace/fixture.json +30 -0
  139. package/fixtures/stacks/cargo-workspace/fixture.json +48 -0
  140. package/fixtures/stacks/django-app/fixture.json +25 -0
  141. package/fixtures/stacks/docker-compose/fixture.json +17 -0
  142. package/fixtures/stacks/dockerfile-service/fixture.json +17 -0
  143. package/fixtures/stacks/dotnet-service/fixture.json +36 -0
  144. package/fixtures/stacks/dotnet-solution-filter/fixture.json +62 -0
  145. package/fixtures/stacks/fastapi-app/fixture.json +29 -0
  146. package/fixtures/stacks/go-workspace/fixture.json +48 -0
  147. package/fixtures/stacks/java-gradle/fixture.json +29 -0
  148. package/fixtures/stacks/java-maven/fixture.json +32 -0
  149. package/fixtures/stacks/kubernetes-helm/fixture.json +25 -0
  150. package/fixtures/stacks/kubernetes-kustomize/fixture.json +21 -0
  151. package/fixtures/stacks/nested-go-workspace/fixture.json +51 -0
  152. package/fixtures/stacks/nextjs-app/fixture.json +34 -0
  153. package/fixtures/stacks/node-turbo-workspace/fixture.json +39 -0
  154. package/fixtures/stacks/pants-python/fixture.json +33 -0
  155. package/fixtures/stacks/php-composer/fixture.json +31 -0
  156. package/fixtures/stacks/python-service/fixture.json +21 -0
  157. package/fixtures/stacks/rails-app/fixture.json +25 -0
  158. package/fixtures/stacks/spring-boot-gradle/fixture.json +29 -0
  159. package/fixtures/stacks/spring-boot-maven/fixture.json +43 -0
  160. package/fixtures/stacks/swift-package/fixture.json +21 -0
  161. package/fixtures/stacks/terraform-module/fixture.json +17 -0
  162. package/fixtures/stacks/uv-python-service/fixture.json +47 -0
  163. package/fixtures/stacks/xcode-app/fixture.json +72 -0
  164. package/package.json +80 -0
  165. package/schemas/patchdrill-doctor.schema.json +171 -0
  166. package/schemas/patchdrill-evidence.schema.json +239 -0
  167. package/schemas/patchdrill-policy.schema.json +170 -0
  168. package/schemas/patchdrill-release-check.schema.json +78 -0
  169. package/schemas/patchdrill-report.schema.json +647 -0
@@ -0,0 +1,206 @@
1
+ import type { Locale } from "./i18n.js";
2
+ export type Severity = "info" | "low" | "medium" | "high" | "critical";
3
+ export type PatchStatus = "pass" | "warn" | "fail";
4
+ export type FileStatus = "added" | "modified" | "deleted" | "renamed" | "copied" | "untracked" | "unknown";
5
+ export interface ChangedFile {
6
+ path: string;
7
+ previousPath?: string;
8
+ status: FileStatus;
9
+ additions: number;
10
+ deletions: number;
11
+ binary: boolean;
12
+ owners?: string[];
13
+ }
14
+ export interface AddedLine {
15
+ file: string;
16
+ line: number;
17
+ content: string;
18
+ }
19
+ export interface ProjectSignal {
20
+ ecosystem: "node" | "python" | "rust" | "go" | "java" | "android" | "ruby" | "php" | "dotnet" | "swift" | "xcode" | "terraform" | "docker" | "kubernetes" | "bazel" | "buck" | "pants" | "github-actions" | "unknown";
21
+ manifestPath: string;
22
+ framework?: "django" | "fastapi" | "spring-boot" | "rails" | "laravel" | "aspnet-core";
23
+ entrypoint?: string;
24
+ packageManager?: string;
25
+ taskRunner?: "turbo" | "nx";
26
+ scripts?: Record<string, string>;
27
+ workspacePackages?: WorkspacePackage[];
28
+ }
29
+ export interface WorkspacePackage {
30
+ name: string;
31
+ projectName?: string;
32
+ path: string;
33
+ scripts: Record<string, string>;
34
+ targets?: string[];
35
+ dependencies?: string[];
36
+ }
37
+ export type DependencyChangeType = "added" | "removed" | "updated";
38
+ export interface DependencyChange {
39
+ file: string;
40
+ packageName: string;
41
+ dependencyType: "dependencies" | "devDependencies" | "peerDependencies" | "optionalDependencies" | "lockfile";
42
+ changeType: DependencyChangeType;
43
+ packagePath?: string;
44
+ before?: string;
45
+ after?: string;
46
+ }
47
+ export type PackageScriptChangeType = "added" | "removed" | "updated";
48
+ export interface PackageScriptChange {
49
+ file: string;
50
+ scriptName: string;
51
+ changeType: PackageScriptChangeType;
52
+ before?: string;
53
+ after?: string;
54
+ }
55
+ export interface CommandPlan {
56
+ id: string;
57
+ label: string;
58
+ command: string;
59
+ reason: string;
60
+ ecosystem: ProjectSignal["ecosystem"] | "general";
61
+ required: boolean;
62
+ packageName?: string;
63
+ packagePath?: string;
64
+ }
65
+ export interface CommandResult {
66
+ id: string;
67
+ command: string;
68
+ exitCode: number;
69
+ durationMs: number;
70
+ stdout: string;
71
+ stderr: string;
72
+ timedOut?: boolean;
73
+ }
74
+ export type VerificationStatus = "passed" | "failed" | "timed-out" | "not-run" | "skipped-optional";
75
+ export interface VerificationSummary {
76
+ plannedRequired: number;
77
+ plannedOptional: number;
78
+ run: number;
79
+ passed: number;
80
+ failed: number;
81
+ timedOut: number;
82
+ missingRequired: number;
83
+ skippedOptional: number;
84
+ unplannedResults: number;
85
+ }
86
+ export interface VerificationCommand {
87
+ id: string;
88
+ label: string;
89
+ command: string;
90
+ reason: string;
91
+ ecosystem: CommandPlan["ecosystem"];
92
+ required: boolean;
93
+ planned: boolean;
94
+ status: VerificationStatus;
95
+ packageName?: string;
96
+ packagePath?: string;
97
+ exitCode?: number;
98
+ durationMs?: number;
99
+ timedOut?: boolean;
100
+ }
101
+ export interface PatchVerification {
102
+ summary: VerificationSummary;
103
+ commands: VerificationCommand[];
104
+ }
105
+ export interface RiskFinding {
106
+ ruleId?: string;
107
+ severity: Severity;
108
+ title: string;
109
+ detail: string;
110
+ file?: string;
111
+ line?: number;
112
+ remediation?: string;
113
+ tags?: string[];
114
+ }
115
+ export interface PolicyRule {
116
+ id: string;
117
+ title: string;
118
+ severity: Severity;
119
+ path?: string | string[];
120
+ detail?: string;
121
+ remediation?: string;
122
+ weight?: number;
123
+ tags?: string[];
124
+ }
125
+ export interface PatchPolicy {
126
+ ignoredPaths: string[];
127
+ failOn?: Severity;
128
+ maxRisk?: number;
129
+ rules: PolicyRule[];
130
+ requiredCommands: CommandPlan[];
131
+ optionalCommands: CommandPlan[];
132
+ }
133
+ export interface PatchSummary {
134
+ status: PatchStatus;
135
+ riskScore: number;
136
+ confidenceScore: number;
137
+ changedFileCount: number;
138
+ additions: number;
139
+ deletions: number;
140
+ requiredCommandCount: number;
141
+ failedCommandCount: number;
142
+ }
143
+ export interface PatchReport {
144
+ schemaVersion: "1";
145
+ generatedAt: string;
146
+ root: string;
147
+ base?: string;
148
+ head?: string;
149
+ summary: PatchSummary;
150
+ changedFiles: ChangedFile[];
151
+ addedLines: number;
152
+ projectSignals: ProjectSignal[];
153
+ affectedPackages: WorkspacePackage[];
154
+ dependencyChanges: DependencyChange[];
155
+ packageScriptChanges: PackageScriptChange[];
156
+ policy?: {
157
+ path: string;
158
+ ignoredPaths: string[];
159
+ failOn?: Severity;
160
+ maxRisk?: number;
161
+ ruleCount: number;
162
+ requiredCommandCount: number;
163
+ optionalCommandCount: number;
164
+ };
165
+ codeOwners?: {
166
+ path: string;
167
+ ruleCount: number;
168
+ };
169
+ baseline?: {
170
+ path: string;
171
+ previousStatus?: PatchStatus;
172
+ currentStatus: PatchStatus;
173
+ previousRiskScore?: number;
174
+ currentRiskScore: number;
175
+ riskDelta: number;
176
+ newFindingCount: number;
177
+ resolvedFindingCount: number;
178
+ unchangedFindingCount: number;
179
+ };
180
+ findings: RiskFinding[];
181
+ commandPlan: CommandPlan[];
182
+ commandResults: CommandResult[];
183
+ verification: PatchVerification;
184
+ }
185
+ export interface ScanOptions {
186
+ cwd: string;
187
+ base?: string;
188
+ head?: string;
189
+ run?: boolean;
190
+ runOptional?: boolean;
191
+ failOn?: Severity;
192
+ configPath?: string;
193
+ baselinePath?: string;
194
+ evidencePath?: string;
195
+ summaryMarkdownPath?: string;
196
+ markdownPath?: string;
197
+ jsonPath?: string;
198
+ sarifPath?: string;
199
+ htmlPath?: string;
200
+ maxOutputChars?: number;
201
+ commandTimeoutMs?: number;
202
+ /** Override the report timestamp for reproducible output; falls back to SOURCE_DATE_EPOCH then wall clock. */
203
+ generatedAt?: string;
204
+ /** Output locale for human-facing artifacts (markdown/summary/html). Default English. */
205
+ locale?: Locale;
206
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { CommandResult, PatchReport, PatchVerification, VerificationCommand, VerificationSummary } from "./types.js";
2
+ export interface VerificationExecution extends VerificationCommand {
3
+ result?: CommandResult;
4
+ }
5
+ export declare function verificationExecutions(report: Pick<PatchReport, "commandPlan" | "commandResults">): VerificationExecution[];
6
+ export declare function verificationSummary(report: Pick<PatchReport, "commandPlan" | "commandResults">): VerificationSummary;
7
+ export declare function formatVerificationStatus(execution: VerificationExecution): string;
8
+ export declare function reportVerification(report: Pick<PatchReport, "commandPlan" | "commandResults">): PatchVerification;
9
+ export declare function withVerification<T extends Omit<PatchReport, "verification">>(report: T): T & {
10
+ verification: PatchVerification;
11
+ };
@@ -0,0 +1,108 @@
1
+ export function verificationExecutions(report) {
2
+ const resultsById = new Map(report.commandResults.map((result) => [result.id, result]));
3
+ const plannedIds = new Set(report.commandPlan.map((plan) => plan.id));
4
+ const executions = report.commandPlan.map((plan) => {
5
+ const result = resultsById.get(plan.id);
6
+ return {
7
+ id: plan.id,
8
+ label: plan.label,
9
+ command: plan.command,
10
+ reason: plan.reason,
11
+ ecosystem: plan.ecosystem,
12
+ required: plan.required,
13
+ planned: true,
14
+ ...(plan.packageName ? { packageName: plan.packageName } : {}),
15
+ ...(plan.packagePath ? { packagePath: plan.packagePath } : {}),
16
+ ...(result ? { result } : {}),
17
+ ...(result ? { exitCode: result.exitCode, durationMs: result.durationMs } : {}),
18
+ ...(result?.timedOut !== undefined ? { timedOut: result.timedOut } : {}),
19
+ status: statusFor(plan, result)
20
+ };
21
+ });
22
+ for (const result of report.commandResults) {
23
+ if (plannedIds.has(result.id))
24
+ continue;
25
+ executions.push({
26
+ id: result.id,
27
+ label: "Unplanned command result",
28
+ command: result.command,
29
+ reason: "A command result was recorded without a matching verification plan entry.",
30
+ ecosystem: "general",
31
+ required: false,
32
+ planned: false,
33
+ result,
34
+ exitCode: result.exitCode,
35
+ durationMs: result.durationMs,
36
+ ...(result.timedOut !== undefined ? { timedOut: result.timedOut } : {}),
37
+ status: statusFor(undefined, result)
38
+ });
39
+ }
40
+ return executions;
41
+ }
42
+ export function verificationSummary(report) {
43
+ const executions = verificationExecutions(report);
44
+ return summarizeExecutions(report.commandPlan, executions);
45
+ }
46
+ function summarizeExecutions(commandPlan, executions) {
47
+ return {
48
+ plannedRequired: commandPlan.filter((command) => command.required).length,
49
+ plannedOptional: commandPlan.filter((command) => !command.required).length,
50
+ run: executions.filter((execution) => execution.result).length,
51
+ passed: executions.filter((execution) => execution.result && execution.status === "passed").length,
52
+ // Status-based so passed + failed + timedOut partitions `run`: a timed-out
53
+ // command has exitCode 124 and must not also be counted as failed.
54
+ failed: executions.filter((execution) => execution.result && execution.status === "failed").length,
55
+ timedOut: executions.filter((execution) => execution.result?.timedOut === true).length,
56
+ missingRequired: executions.filter((execution) => execution.status === "not-run").length,
57
+ skippedOptional: executions.filter((execution) => execution.status === "skipped-optional").length,
58
+ unplannedResults: executions.filter((execution) => !execution.planned).length
59
+ };
60
+ }
61
+ export function formatVerificationStatus(execution) {
62
+ const prefix = execution.planned ? "" : "unplanned ";
63
+ if (!execution.result)
64
+ return execution.status === "skipped-optional" ? "skipped optional" : "not run";
65
+ if (execution.result.timedOut)
66
+ return `${prefix}timed out (${execution.result.exitCode})`;
67
+ if (execution.result.exitCode === 0)
68
+ return `${prefix}passed`;
69
+ return `${prefix}failed (${execution.result.exitCode})`;
70
+ }
71
+ export function reportVerification(report) {
72
+ const executions = verificationExecutions(report);
73
+ return {
74
+ summary: summarizeExecutions(report.commandPlan, executions),
75
+ commands: executions.map(toVerificationCommand)
76
+ };
77
+ }
78
+ export function withVerification(report) {
79
+ return {
80
+ ...report,
81
+ verification: reportVerification(report)
82
+ };
83
+ }
84
+ function toVerificationCommand(execution) {
85
+ return {
86
+ id: execution.id,
87
+ label: execution.label,
88
+ command: execution.command,
89
+ reason: execution.reason,
90
+ ecosystem: execution.ecosystem,
91
+ required: execution.required,
92
+ planned: execution.planned,
93
+ status: execution.status,
94
+ ...(execution.packageName ? { packageName: execution.packageName } : {}),
95
+ ...(execution.packagePath ? { packagePath: execution.packagePath } : {}),
96
+ ...(execution.exitCode !== undefined ? { exitCode: execution.exitCode } : {}),
97
+ ...(execution.durationMs !== undefined ? { durationMs: execution.durationMs } : {}),
98
+ ...(execution.timedOut !== undefined ? { timedOut: execution.timedOut } : {})
99
+ };
100
+ }
101
+ function statusFor(plan, result) {
102
+ if (!result)
103
+ return plan?.required ? "not-run" : "skipped-optional";
104
+ if (result.timedOut)
105
+ return "timed-out";
106
+ return result.exitCode === 0 ? "passed" : "failed";
107
+ }
108
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../src/verification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ # GitHub Actions Annotations
2
+
3
+ PatchDrill can emit GitHub Actions workflow-command annotations for findings:
4
+
5
+ ```bash
6
+ patchdrill scan --base origin/main --github-annotations
7
+ ```
8
+
9
+ The composite Action enables annotations by default:
10
+
11
+ ```yaml
12
+ - uses: seungdori/patchdrill@v0
13
+ with:
14
+ base: origin/${{ github.base_ref }}
15
+ annotations: "true"
16
+ ```
17
+
18
+ `patchdrill init` writes this setting explicitly in the generated workflow so reviewers can see that Checks annotations are part of the default PR evidence.
19
+
20
+ Set `annotations: "false"` to disable Checks annotations. The Action accepts `"true"`, `"false"`, `"1"`, `"0"`, `"yes"`, `"no"`, `"on"`, and `"off"` for boolean inputs.
21
+
22
+ These annotations appear in the Actions log and Checks UI. They are meant for immediate review attention, while Markdown, JSON, SARIF, and HTML remain the durable artifacts.
23
+
24
+ ## Severity Mapping
25
+
26
+ | PatchDrill | GitHub annotation |
27
+ | --- | --- |
28
+ | `critical` | `error` |
29
+ | `high` | `error` |
30
+ | `medium` | `warning` |
31
+ | `low` | `notice` |
32
+ | `info` | `notice` |
33
+
34
+ File-scoped findings include file and line metadata when available. Global findings are emitted without a file property.
@@ -0,0 +1,79 @@
1
+ # Architecture
2
+
3
+ PatchDrill is split into deterministic modules:
4
+
5
+ | Module | Responsibility |
6
+ | --- | --- |
7
+ | `src/baseline.ts` | Compares current reports with previous JSON baselines and computes risk deltas. |
8
+ | `src/codeowners.ts` | Reads GitHub CODEOWNERS files and annotates changed files with owners. |
9
+ | `src/command-plan.ts` | Normalizes verification command plans, deduplicates matching commands, and preserves required-command strength when policy and detectors overlap. |
10
+ | `src/git.ts` | Reads changed files from git ranges, staged changes, unstaged changes, and untracked files. |
11
+ | `src/policy.ts` | Loads `.patchdrill.yml/json`, filters ignored paths, and merges repo-specific commands/rules. |
12
+ | `src/project.ts` | Discovers ecosystem signals, nested project roots, package managers, task runners, solution filters, Xcode containers, and workspace dependency graphs from manifests. |
13
+ | `src/dependency.ts` | Extracts package.json, pyproject.toml, requirements.txt, NuGet PackageReference/PackageVersion, Maven pom.xml, Gradle build files and version catalogs, Gemfile, composer.json, go.mod, Cargo.toml, npm, pnpm, Yarn, Bun, Go, Cargo, Poetry, uv, Pipfile, Bundler, and Composer dependency additions, removals, and version updates through a parser/diff analyzer registry. |
14
+ | `src/doctor.ts` | Renders first-run repository readiness diagnostics without mutating the repository or running verification commands. |
15
+ | `src/markdown-links.ts` | Checks public README, docs, and example Markdown local links so launch documentation cannot drift silently. |
16
+ | `src/package-scripts.ts` | Extracts package.json script additions, removals, and updates so risk scoring can distinguish dependency intent from executable package automation changes. |
17
+ | `src/evidence.ts` | Renders and verifies Proof Pack evidence manifests with tool version, report metadata, artifact, and command-output digests. |
18
+ | `src/planner.ts` | Turns changed files, workspace package impact, project signals, nested package scopes, and platform metadata into a verification command plan through ecosystem planner handlers. |
19
+ | `src/risk.ts` | Scores the patch and emits explainable findings, including missing required verification evidence, dependency proof gaps, and whole-workflow GitHub Actions trust-boundary checks. |
20
+ | `src/runner.ts` | Executes required commands when `--run` is set and optional commands when `--run-optional` is also set. |
21
+ | `src/verification.ts` | Joins verification plans with command results into passed, failed, timed-out, not-run, skipped-optional, and unplanned execution states for human reports and JSON automation consumers. |
22
+ | `src/release-readiness.ts` | Performs local static release-readiness checks for npm package metadata, action wiring, provenance workflow settings, public release docs, demo artifact synchronization, and local Markdown links. |
23
+ | `src/report-contract.ts` | Verifies JSON report self-consistency, including summary counts derived from changed files, command plans, and command results. |
24
+ | `src/report.ts` | Renders Markdown summaries, evaluates fail thresholds, and keeps the public report-renderer re-export surface stable. |
25
+ | `src/report-annotations.ts` | Renders escaped GitHub Actions annotation commands from findings. |
26
+ | `src/report-html.ts` | Renders the self-contained Proof Pack HTML dashboard and run-trend view. |
27
+ | `src/report-sarif.ts` | Renders SARIF 2.1.0 output and stable finding fingerprints for GitHub code scanning. |
28
+ | `src/schema.ts` | Exposes embedded JSON Schemas for policy, report, evidence, doctor, and release-check contracts. |
29
+ | `src/scan.ts` | Orchestrates the scan pipeline. |
30
+ | `src/stack-coverage.ts` | Defines the public fixture-backed stack coverage matrix used by launch docs and tests. |
31
+ | `src/cli.ts` | Parses arguments and handles user output. |
32
+
33
+ ## Pipeline
34
+
35
+ ```text
36
+ git diff -> changed files + added lines -> policy filters -> CODEOWNERS hints
37
+ | |
38
+ v v
39
+ project signals + affected packages -> verification command plan
40
+ |
41
+ v
42
+ dependency diff + package automation enrichment
43
+ |
44
+ v
45
+ opt-in command runner
46
+ |
47
+ v
48
+ command plan + command results -> verification status matrix
49
+ |
50
+ v
51
+ risk assessment -> baseline comparison
52
+ |
53
+ v
54
+ Proof Pack: Markdown / JSON / SARIF / HTML / evidence
55
+ |
56
+ v
57
+ fail-on severity + max-risk + max-risk-delta gate
58
+ ```
59
+
60
+ ## Extension Seams
61
+
62
+ - Dependency formats register a name, matcher, parser, diff function, and empty snapshot in `src/dependency.ts`, so adding a manifest or lockfile format does not grow the scan orchestrator and can be reflected in coverage docs.
63
+ - Verification command adapters add plans through `src/command-plan.ts`, so duplicate detector and policy commands are normalized before running or scoring evidence gaps.
64
+ - Report contract checks live in `src/report-contract.ts`, so evidence verification can reject a JSON report whose summary counts or computed verification status no longer match its payload.
65
+ - Project and workspace detection lives in `src/project.ts`; command planning consumes those signals through the handler registry in `src/planner.ts`.
66
+ - Risk scoring stays explainable: every score increase in `src/risk.ts` must produce a human-readable finding and a stable rule ID documented in [RULE_CATALOG.md](RULE_CATALOG.md).
67
+
68
+ ## Non-Goals
69
+
70
+ - Replacing human code review.
71
+ - Calling an LLM by default.
72
+ - Running destructive commands.
73
+ - Becoming a full SAST, SCA, or test selection platform.
74
+
75
+ PatchDrill should stay useful as the small, deterministic layer before those heavier tools.
76
+
77
+ ## Security Posture
78
+
79
+ The repository also includes CI, CodeQL, OpenSSF Scorecard, Dependabot, and package dry-run verification. See [SECURITY_POSTURE.md](SECURITY_POSTURE.md).
@@ -0,0 +1,32 @@
1
+ # Baselines
2
+
3
+ PatchDrill can compare a scan against a previous JSON report.
4
+
5
+ ```bash
6
+ patchdrill scan \
7
+ --baseline previous-patchdrill-report.json \
8
+ --max-risk-delta 0 \
9
+ --json patchdrill-report.json \
10
+ --markdown patchdrill-report.md
11
+ ```
12
+
13
+ The report includes:
14
+
15
+ - Previous and current assessment status.
16
+ - Previous and current risk score.
17
+ - Risk delta.
18
+ - New, resolved, and unchanged finding counts.
19
+
20
+ Findings are compared by deterministic fingerprints built from rule id, severity, title, file, and line. The comparison is local and does not upload source code or reports.
21
+
22
+ Use `--max-risk-delta 0` to fail on any risk increase, or a higher value to allow small expected increases while blocking larger regressions.
23
+
24
+ In GitHub Actions, pass the baseline path after downloading or restoring a previous report artifact:
25
+
26
+ ```yaml
27
+ - uses: seungdori/patchdrill@v0
28
+ with:
29
+ baseline: previous-patchdrill-report.json
30
+ max-risk-delta: "0"
31
+ json: patchdrill-report.json
32
+ ```
@@ -0,0 +1,106 @@
1
+ # Case Studies
2
+
3
+ These case studies are designed for launch readers who ask, "What does PatchDrill catch that a normal CI green check or model review might not make obvious?"
4
+
5
+ They are representative and deterministic. Each one maps a patch shape to the Proof Pack evidence PatchDrill emits: risk findings, command plan, report artifacts, and evidence verification.
6
+
7
+ ## Risky Agent PR
8
+
9
+ Artifact path: [examples/risky-agent-pr](../examples/risky-agent-pr)
10
+
11
+ Patch shape:
12
+
13
+ - Privileged `pull_request_target` workflow behavior.
14
+ - PR-head checkout inside a privileged workflow.
15
+ - Secret-looking environment example.
16
+ - package.json lifecycle script addition.
17
+ - Required verification commands that should be reviewed before merge.
18
+
19
+ PatchDrill output:
20
+
21
+ - Critical workflow trust-boundary finding.
22
+ - Secret-looking value finding.
23
+ - Package lifecycle script finding.
24
+ - SARIF output for GitHub code scanning.
25
+ - Compact PR summary for reviewers.
26
+ - HTML dashboard for a self-contained Proof Pack.
27
+
28
+ Why it matters:
29
+
30
+ Traditional CI can pass while the workflow permission boundary becomes unsafe. PatchDrill makes that boundary visible before a reviewer has to manually audit every workflow line.
31
+
32
+ ## Review-Ready Proof Pack
33
+
34
+ Artifact path: [examples/demo](../examples/demo)
35
+
36
+ Patch shape:
37
+
38
+ - Product source changes.
39
+ - Workspace-scoped package impact.
40
+ - Required and optional command plan.
41
+ - No command failures.
42
+
43
+ PatchDrill output:
44
+
45
+ - Markdown report for human review.
46
+ - JSON report for bots.
47
+ - SARIF report for code scanning.
48
+ - HTML dashboard.
49
+ - Compact PR summary.
50
+
51
+ Why it matters:
52
+
53
+ This is the "normal" case: PatchDrill does not need to invent drama. It gives reviewers a stable bundle that says what changed, what should be tested, and what residual risk remains.
54
+
55
+ ## Dependency Proof Gap
56
+
57
+ Patch shape:
58
+
59
+ - Manifest dependency changed without a matching lockfile change.
60
+ - Or lockfile resolution changed without a matching manifest intent.
61
+
62
+ PatchDrill output:
63
+
64
+ - Dependency diff table with package, section, before, and after.
65
+ - Proof-gap finding that explains whether intent or resolution evidence is missing.
66
+ - Command plan still derived from the touched ecosystem.
67
+
68
+ Why it matters:
69
+
70
+ SCA tools answer vulnerability questions. PatchDrill answers a reviewer question: "Does this dependency change include enough intent and resolution evidence to trust the patch?"
71
+
72
+ ## Package Script Tampering
73
+
74
+ Patch shape:
75
+
76
+ - A test/lint/build script is removed, replaced with a no-op, or weakened.
77
+ - An install/prepare/pack/publish lifecycle hook is added.
78
+ - A script pipes a remote download into a shell or interpreter.
79
+
80
+ PatchDrill output:
81
+
82
+ - Structured package script change summary.
83
+ - Risk findings for lifecycle hooks, removed verification scripts, no-op checks, and remote shell pipes.
84
+ - Human-readable remediation in Markdown and SARIF.
85
+
86
+ Why it matters:
87
+
88
+ AI-authored patches can make a PR look green by weakening the command that CI already trusts. PatchDrill treats the command definition itself as review evidence.
89
+
90
+ ## Workflow OIDC Boundary
91
+
92
+ Patch shape:
93
+
94
+ - GitHub Actions job grants OIDC permissions.
95
+ - Remote reusable workflow receives inherited secrets or caller permissions.
96
+ - Environment protection is absent or unclear.
97
+
98
+ PatchDrill output:
99
+
100
+ - Trust-boundary findings for OIDC, secrets inheritance, mutable reusable workflow refs, and privileged trigger combinations.
101
+ - Local reusable workflow expansion for downstream analysis.
102
+ - SARIF and annotation output for review surfaces.
103
+
104
+ Why it matters:
105
+
106
+ OIDC and reusable workflow changes are easy to miss in code review because the risky behavior can be spread across several workflow files. PatchDrill loads local reusable workflow references and reports the combined boundary.
@@ -0,0 +1,23 @@
1
+ # CODEOWNERS
2
+
3
+ PatchDrill reads GitHub CODEOWNERS files and adds owner hints to changed files in Markdown and JSON reports.
4
+
5
+ Search order matches GitHub:
6
+
7
+ - `.github/CODEOWNERS`
8
+ - `CODEOWNERS`
9
+ - `docs/CODEOWNERS`
10
+
11
+ When multiple rules match a file, the last matching rule wins. A matching rule with no owners clears owners for that path, which mirrors common CODEOWNERS usage for subdirectory exceptions.
12
+
13
+ ## Supported Syntax
14
+
15
+ PatchDrill supports the common GitHub CODEOWNERS subset:
16
+
17
+ - `*`, `?`, and `**` wildcards.
18
+ - Root-anchored patterns such as `/src/`.
19
+ - Directory patterns such as `apps/`.
20
+ - Inline comments after owner tokens.
21
+ - GitHub users, teams, and email owners as raw strings.
22
+
23
+ PatchDrill skips unsupported negation (`!`) and bracket range patterns (`[a-z]`), matching GitHub's documented CODEOWNERS limitations.