vaspera 2.9.0 → 2.10.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/CHANGELOG.md +122 -7
- package/README.md +58 -1
- package/dist/__tests__/autofix/branch-manager.test.d.ts +2 -0
- package/dist/__tests__/autofix/branch-manager.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/branch-manager.test.js +60 -0
- package/dist/__tests__/autofix/branch-manager.test.js.map +1 -0
- package/dist/__tests__/autofix/commit-generator.test.d.ts +2 -0
- package/dist/__tests__/autofix/commit-generator.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/commit-generator.test.js +147 -0
- package/dist/__tests__/autofix/commit-generator.test.js.map +1 -0
- package/dist/__tests__/autofix/constitution.test.d.ts +9 -0
- package/dist/__tests__/autofix/constitution.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/constitution.test.js +421 -0
- package/dist/__tests__/autofix/constitution.test.js.map +1 -0
- package/dist/__tests__/autofix/pr-generator.test.d.ts +2 -0
- package/dist/__tests__/autofix/pr-generator.test.d.ts.map +1 -0
- package/dist/__tests__/autofix/pr-generator.test.js +152 -0
- package/dist/__tests__/autofix/pr-generator.test.js.map +1 -0
- package/dist/__tests__/property-test-helpers.d.ts +87 -0
- package/dist/__tests__/property-test-helpers.d.ts.map +1 -0
- package/dist/__tests__/property-test-helpers.js +136 -0
- package/dist/__tests__/property-test-helpers.js.map +1 -0
- package/dist/__tests__/scanners/dast/index.test.d.ts +2 -0
- package/dist/__tests__/scanners/dast/index.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/dast/index.test.js +183 -0
- package/dist/__tests__/scanners/dast/index.test.js.map +1 -0
- package/dist/__tests__/scanners/dast/nuclei.test.d.ts +2 -0
- package/dist/__tests__/scanners/dast/nuclei.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/dast/nuclei.test.js +166 -0
- package/dist/__tests__/scanners/dast/nuclei.test.js.map +1 -0
- package/dist/__tests__/scanners/dast/zap.test.d.ts +2 -0
- package/dist/__tests__/scanners/dast/zap.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/dast/zap.test.js +158 -0
- package/dist/__tests__/scanners/dast/zap.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-feedback.test.d.ts +2 -0
- package/dist/__tests__/scanners/fp-feedback.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-feedback.test.js +202 -0
- package/dist/__tests__/scanners/fp-feedback.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-filter.property.test.d.ts +9 -0
- package/dist/__tests__/scanners/fp-filter.property.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-filter.property.test.js +253 -0
- package/dist/__tests__/scanners/fp-filter.property.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-filter.test.d.ts +2 -0
- package/dist/__tests__/scanners/fp-filter.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-filter.test.js +234 -0
- package/dist/__tests__/scanners/fp-filter.test.js.map +1 -0
- package/dist/__tests__/scanners/fp-tracker.test.d.ts +2 -0
- package/dist/__tests__/scanners/fp-tracker.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/fp-tracker.test.js +262 -0
- package/dist/__tests__/scanners/fp-tracker.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts +10 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js +238 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts +2 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js +55 -0
- package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/index.test.d.ts +2 -0
- package/dist/__tests__/scanners/logic/index.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/index.test.js +165 -0
- package/dist/__tests__/scanners/logic/index.test.js.map +1 -0
- package/dist/__tests__/scanners/logic/types.test.d.ts +2 -0
- package/dist/__tests__/scanners/logic/types.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/logic/types.test.js +85 -0
- package/dist/__tests__/scanners/logic/types.test.js.map +1 -0
- package/dist/action/pr-comment.test.js +4 -0
- package/dist/action/pr-comment.test.js.map +1 -1
- package/dist/action/sarif-upload.test.js +4 -0
- package/dist/action/sarif-upload.test.js.map +1 -1
- package/dist/autofix/branch-manager.d.ts +115 -0
- package/dist/autofix/branch-manager.d.ts.map +1 -0
- package/dist/autofix/branch-manager.js +308 -0
- package/dist/autofix/branch-manager.js.map +1 -0
- package/dist/autofix/commit-generator.d.ts +55 -0
- package/dist/autofix/commit-generator.d.ts.map +1 -0
- package/dist/autofix/commit-generator.js +277 -0
- package/dist/autofix/commit-generator.js.map +1 -0
- package/dist/autofix/constitution.d.ts +77 -0
- package/dist/autofix/constitution.d.ts.map +1 -0
- package/dist/autofix/constitution.js +261 -0
- package/dist/autofix/constitution.js.map +1 -0
- package/dist/autofix/constitution.schema.d.ts +441 -0
- package/dist/autofix/constitution.schema.d.ts.map +1 -0
- package/dist/autofix/constitution.schema.js +144 -0
- package/dist/autofix/constitution.schema.js.map +1 -0
- package/dist/autofix/index.d.ts +13 -0
- package/dist/autofix/index.d.ts.map +1 -0
- package/dist/autofix/index.js +15 -0
- package/dist/autofix/index.js.map +1 -0
- package/dist/autofix/pr-generator.d.ts +57 -0
- package/dist/autofix/pr-generator.d.ts.map +1 -0
- package/dist/autofix/pr-generator.js +597 -0
- package/dist/autofix/pr-generator.js.map +1 -0
- package/dist/autofix/types.d.ts +151 -0
- package/dist/autofix/types.d.ts.map +1 -0
- package/dist/autofix/types.js +22 -0
- package/dist/autofix/types.js.map +1 -0
- package/dist/eval/fixtures.d.ts +20 -0
- package/dist/eval/fixtures.d.ts.map +1 -1
- package/dist/eval/fixtures.js +430 -0
- package/dist/eval/fixtures.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -1
- package/dist/index.js.map +1 -1
- package/dist/scanners/cache.d.ts.map +1 -1
- package/dist/scanners/cache.js +4 -0
- package/dist/scanners/cache.js.map +1 -1
- package/dist/scanners/dast/index.d.ts +39 -0
- package/dist/scanners/dast/index.d.ts.map +1 -0
- package/dist/scanners/dast/index.js +259 -0
- package/dist/scanners/dast/index.js.map +1 -0
- package/dist/scanners/dast/nuclei.d.ts +26 -0
- package/dist/scanners/dast/nuclei.d.ts.map +1 -0
- package/dist/scanners/dast/nuclei.js +354 -0
- package/dist/scanners/dast/nuclei.js.map +1 -0
- package/dist/scanners/dast/types.d.ts +306 -0
- package/dist/scanners/dast/types.d.ts.map +1 -0
- package/dist/scanners/dast/types.js +52 -0
- package/dist/scanners/dast/types.js.map +1 -0
- package/dist/scanners/dast/zap.d.ts +26 -0
- package/dist/scanners/dast/zap.d.ts.map +1 -0
- package/dist/scanners/dast/zap.js +453 -0
- package/dist/scanners/dast/zap.js.map +1 -0
- package/dist/scanners/fp-feedback.d.ts +140 -0
- package/dist/scanners/fp-feedback.d.ts.map +1 -0
- package/dist/scanners/fp-feedback.js +292 -0
- package/dist/scanners/fp-feedback.js.map +1 -0
- package/dist/scanners/fp-filter.d.ts +94 -0
- package/dist/scanners/fp-filter.d.ts.map +1 -0
- package/dist/scanners/fp-filter.js +397 -0
- package/dist/scanners/fp-filter.js.map +1 -0
- package/dist/scanners/fp-tracker.d.ts +125 -0
- package/dist/scanners/fp-tracker.d.ts.map +1 -0
- package/dist/scanners/fp-tracker.js +330 -0
- package/dist/scanners/fp-tracker.js.map +1 -0
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +56 -0
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/index.test.js +6 -6
- package/dist/scanners/index.test.js.map +1 -1
- package/dist/scanners/logic/auth-flow-analyzer.d.ts +18 -0
- package/dist/scanners/logic/auth-flow-analyzer.d.ts.map +1 -0
- package/dist/scanners/logic/auth-flow-analyzer.js +384 -0
- package/dist/scanners/logic/auth-flow-analyzer.js.map +1 -0
- package/dist/scanners/logic/endpoint-analyzer.d.ts +29 -0
- package/dist/scanners/logic/endpoint-analyzer.d.ts.map +1 -0
- package/dist/scanners/logic/endpoint-analyzer.js +528 -0
- package/dist/scanners/logic/endpoint-analyzer.js.map +1 -0
- package/dist/scanners/logic/index.d.ts +41 -0
- package/dist/scanners/logic/index.d.ts.map +1 -0
- package/dist/scanners/logic/index.js +268 -0
- package/dist/scanners/logic/index.js.map +1 -0
- package/dist/scanners/logic/types.d.ts +254 -0
- package/dist/scanners/logic/types.d.ts.map +1 -0
- package/dist/scanners/logic/types.js +142 -0
- package/dist/scanners/logic/types.js.map +1 -0
- package/dist/scanners/types.d.ts +1 -1
- package/dist/scanners/types.d.ts.map +1 -1
- package/dist/scanners/types.js +4 -0
- package/dist/scanners/types.js.map +1 -1
- package/dist/telemetry/usage.d.ts +1 -1
- package/dist/telemetry/usage.d.ts.map +1 -1
- package/dist/telemetry/usage.js +14 -6
- package/dist/telemetry/usage.js.map +1 -1
- package/package.json +6 -8
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles git branch operations for autofix PRs.
|
|
5
|
+
* Uses cross-spawn with array args per CLAUDE.md guidelines.
|
|
6
|
+
*
|
|
7
|
+
* @module autofix/branch-manager
|
|
8
|
+
*/
|
|
9
|
+
import spawn from "cross-spawn";
|
|
10
|
+
import { logger } from "../logger.js";
|
|
11
|
+
/**
|
|
12
|
+
* Execute a git command and return the result
|
|
13
|
+
*/
|
|
14
|
+
export async function git(args, options = { cwd: process.cwd() }) {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
const child = spawn("git", args, {
|
|
17
|
+
cwd: options.cwd,
|
|
18
|
+
timeout: options.timeout ?? 30000,
|
|
19
|
+
});
|
|
20
|
+
let stdout = "";
|
|
21
|
+
let stderr = "";
|
|
22
|
+
child.stdout?.on("data", (data) => {
|
|
23
|
+
stdout += data.toString();
|
|
24
|
+
});
|
|
25
|
+
child.stderr?.on("data", (data) => {
|
|
26
|
+
stderr += data.toString();
|
|
27
|
+
});
|
|
28
|
+
child.on("close", (exitCode) => {
|
|
29
|
+
const success = exitCode === 0;
|
|
30
|
+
if (!success) {
|
|
31
|
+
logger.debug("git.command_failed", {
|
|
32
|
+
args: args.join(" "),
|
|
33
|
+
exitCode,
|
|
34
|
+
stderr: stderr.slice(0, 500),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
resolve({
|
|
38
|
+
success,
|
|
39
|
+
stdout: stdout.trim(),
|
|
40
|
+
stderr: stderr.trim(),
|
|
41
|
+
exitCode: exitCode ?? undefined,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
child.on("error", (error) => {
|
|
45
|
+
logger.error("git.spawn_error", {
|
|
46
|
+
args: args.join(" "),
|
|
47
|
+
error: String(error),
|
|
48
|
+
});
|
|
49
|
+
resolve({
|
|
50
|
+
success: false,
|
|
51
|
+
stdout,
|
|
52
|
+
stderr,
|
|
53
|
+
error: String(error),
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if git is available
|
|
60
|
+
*/
|
|
61
|
+
export async function isGitAvailable(cwd) {
|
|
62
|
+
const result = await git(["--version"], { cwd, timeout: 5000 });
|
|
63
|
+
return result.success;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if directory is a git repository
|
|
67
|
+
*/
|
|
68
|
+
export async function isGitRepo(cwd) {
|
|
69
|
+
const result = await git(["rev-parse", "--is-inside-work-tree"], { cwd, timeout: 5000 });
|
|
70
|
+
return result.success && result.stdout === "true";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the current branch name
|
|
74
|
+
*/
|
|
75
|
+
export async function getCurrentBranch(cwd) {
|
|
76
|
+
const result = await git(["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
|
|
77
|
+
return result.success ? result.stdout : undefined;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the default branch name (main or master)
|
|
81
|
+
*/
|
|
82
|
+
export async function getDefaultBranch(cwd) {
|
|
83
|
+
// Try to get from remote HEAD reference
|
|
84
|
+
const remoteResult = await git(["symbolic-ref", "refs/remotes/origin/HEAD", "--short"], { cwd });
|
|
85
|
+
if (remoteResult.success && remoteResult.stdout) {
|
|
86
|
+
// Returns something like "origin/main", extract just "main"
|
|
87
|
+
const parts = remoteResult.stdout.split("/");
|
|
88
|
+
return parts[parts.length - 1];
|
|
89
|
+
}
|
|
90
|
+
// Fallback: check if main exists
|
|
91
|
+
const mainResult = await git(["show-ref", "--verify", "--quiet", "refs/heads/main"], { cwd });
|
|
92
|
+
if (mainResult.success) {
|
|
93
|
+
return "main";
|
|
94
|
+
}
|
|
95
|
+
// Final fallback
|
|
96
|
+
return "master";
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if a branch exists locally
|
|
100
|
+
*/
|
|
101
|
+
export async function branchExists(cwd, branchName) {
|
|
102
|
+
const result = await git(["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`], { cwd });
|
|
103
|
+
return result.success;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check if a branch exists on remote
|
|
107
|
+
*/
|
|
108
|
+
export async function remoteBranchExists(cwd, branchName, remote = "origin") {
|
|
109
|
+
const result = await git(["ls-remote", "--heads", remote, branchName], { cwd, timeout: 30000 });
|
|
110
|
+
return result.success && !!result.stdout?.includes(branchName);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a new branch from a base
|
|
114
|
+
*/
|
|
115
|
+
export async function createBranch(cwd, branchName, baseBranch) {
|
|
116
|
+
logger.info("git.create_branch", { branchName, baseBranch });
|
|
117
|
+
// First ensure we have latest base branch
|
|
118
|
+
await git(["fetch", "origin", baseBranch], { cwd });
|
|
119
|
+
// Create branch from remote base
|
|
120
|
+
return git(["checkout", "-b", branchName, `origin/${baseBranch}`], { cwd });
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Switch to an existing branch
|
|
124
|
+
*/
|
|
125
|
+
export async function checkoutBranch(cwd, branchName) {
|
|
126
|
+
logger.debug("git.checkout", { branchName });
|
|
127
|
+
return git(["checkout", branchName], { cwd });
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Delete a local branch
|
|
131
|
+
*/
|
|
132
|
+
export async function deleteBranch(cwd, branchName, force = false) {
|
|
133
|
+
logger.info("git.delete_branch", { branchName, force });
|
|
134
|
+
const flag = force ? "-D" : "-d";
|
|
135
|
+
return git(["branch", flag, branchName], { cwd });
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Stage specific files
|
|
139
|
+
*/
|
|
140
|
+
export async function stageFiles(cwd, files) {
|
|
141
|
+
if (files.length === 0) {
|
|
142
|
+
return { success: true, stdout: "", stderr: "" };
|
|
143
|
+
}
|
|
144
|
+
logger.debug("git.stage_files", { count: files.length });
|
|
145
|
+
return git(["add", ...files], { cwd });
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get list of modified files (staged and unstaged)
|
|
149
|
+
*/
|
|
150
|
+
export async function getModifiedFiles(cwd) {
|
|
151
|
+
const result = await git(["status", "--porcelain"], { cwd });
|
|
152
|
+
if (!result.success) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return (result.stdout ?? "")
|
|
156
|
+
.split("\n")
|
|
157
|
+
.filter((line) => line.length > 0)
|
|
158
|
+
.map((line) => line.slice(3).trim());
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get list of staged files
|
|
162
|
+
*/
|
|
163
|
+
export async function getStagedFiles(cwd) {
|
|
164
|
+
const result = await git(["diff", "--cached", "--name-only"], { cwd });
|
|
165
|
+
if (!result.success) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
return (result.stdout ?? "").split("\n").filter((line) => line.length > 0);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Check if working tree is clean
|
|
172
|
+
*/
|
|
173
|
+
export async function isWorkingTreeClean(cwd) {
|
|
174
|
+
const result = await git(["status", "--porcelain"], { cwd });
|
|
175
|
+
return result.success && result.stdout === "";
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Push branch to remote
|
|
179
|
+
*/
|
|
180
|
+
export async function pushBranch(cwd, branchName, remote = "origin", setUpstream = true) {
|
|
181
|
+
logger.info("git.push", { branchName, remote, setUpstream });
|
|
182
|
+
const args = ["push"];
|
|
183
|
+
if (setUpstream) {
|
|
184
|
+
args.push("-u");
|
|
185
|
+
}
|
|
186
|
+
args.push(remote, branchName);
|
|
187
|
+
return git(args, { cwd, timeout: 60000 });
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Stash current changes
|
|
191
|
+
*/
|
|
192
|
+
export async function stash(cwd, message) {
|
|
193
|
+
const args = ["stash", "push"];
|
|
194
|
+
if (message) {
|
|
195
|
+
args.push("-m", message);
|
|
196
|
+
}
|
|
197
|
+
return git(args, { cwd });
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Pop stashed changes
|
|
201
|
+
*/
|
|
202
|
+
export async function stashPop(cwd) {
|
|
203
|
+
return git(["stash", "pop"], { cwd });
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get branch info including tracking
|
|
207
|
+
*/
|
|
208
|
+
export async function getBranchInfo(cwd, branchName) {
|
|
209
|
+
const name = branchName ?? (await getCurrentBranch(cwd));
|
|
210
|
+
if (!name) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const current = (await getCurrentBranch(cwd)) === name;
|
|
214
|
+
// Get tracking info
|
|
215
|
+
const trackingResult = await git(["rev-parse", "--abbrev-ref", `${name}@{upstream}`], { cwd });
|
|
216
|
+
const tracking = trackingResult.success ? trackingResult.stdout : undefined;
|
|
217
|
+
// Get ahead/behind counts
|
|
218
|
+
let ahead = 0;
|
|
219
|
+
let behind = 0;
|
|
220
|
+
if (tracking) {
|
|
221
|
+
const countResult = await git(["rev-list", "--left-right", "--count", `${name}...${tracking}`], { cwd });
|
|
222
|
+
if (countResult.success && countResult.stdout) {
|
|
223
|
+
const [a, b] = countResult.stdout.split("\t").map(Number);
|
|
224
|
+
ahead = a || 0;
|
|
225
|
+
behind = b || 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
name,
|
|
230
|
+
current,
|
|
231
|
+
remote: tracking ? tracking.split("/")[0] : undefined,
|
|
232
|
+
tracking,
|
|
233
|
+
ahead,
|
|
234
|
+
behind,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Reset branch to a specific commit
|
|
239
|
+
*/
|
|
240
|
+
export async function resetToCommit(cwd, commitish, mode = "mixed") {
|
|
241
|
+
logger.warn("git.reset", { commitish, mode });
|
|
242
|
+
return git(["reset", `--${mode}`, commitish], { cwd });
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get the remote URL for origin
|
|
246
|
+
*/
|
|
247
|
+
export async function getRemoteUrl(cwd, remote = "origin") {
|
|
248
|
+
const result = await git(["remote", "get-url", remote], { cwd });
|
|
249
|
+
return result.success ? result.stdout : undefined;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Parse GitHub owner/repo from remote URL
|
|
253
|
+
*/
|
|
254
|
+
export function parseGitHubRemote(url) {
|
|
255
|
+
// SSH format: git@github.com:owner/repo.git
|
|
256
|
+
const sshMatch = url.match(/git@github\.com:([^/]+)\/(.+?)(\.git)?$/);
|
|
257
|
+
if (sshMatch) {
|
|
258
|
+
return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
259
|
+
}
|
|
260
|
+
// HTTPS format: https://github.com/owner/repo.git
|
|
261
|
+
const httpsMatch = url.match(/https:\/\/github\.com\/([^/]+)\/(.+?)(\.git)?$/);
|
|
262
|
+
if (httpsMatch) {
|
|
263
|
+
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Generate a unique branch name
|
|
269
|
+
*/
|
|
270
|
+
export function generateBranchName(prefix, identifier) {
|
|
271
|
+
const timestamp = Date.now().toString(36);
|
|
272
|
+
const sanitized = identifier
|
|
273
|
+
.toLowerCase()
|
|
274
|
+
.replace(/[^a-z0-9-]/g, "-")
|
|
275
|
+
.replace(/-+/g, "-")
|
|
276
|
+
.slice(0, 30);
|
|
277
|
+
return `${prefix}/${sanitized}-${timestamp}`;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Ensure we're on a clean working tree before operations
|
|
281
|
+
*/
|
|
282
|
+
export async function ensureCleanWorkingTree(cwd, stashIfDirty = true) {
|
|
283
|
+
const clean = await isWorkingTreeClean(cwd);
|
|
284
|
+
if (clean) {
|
|
285
|
+
return { clean: true, stashed: false };
|
|
286
|
+
}
|
|
287
|
+
if (stashIfDirty) {
|
|
288
|
+
const result = await stash(cwd, "vaspera-autofix: temporary stash");
|
|
289
|
+
if (result.success) {
|
|
290
|
+
logger.info("git.stashed_changes", { message: "Working tree had changes, stashed" });
|
|
291
|
+
return { clean: true, stashed: true };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { clean: false, stashed: false };
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Restore original branch state after operations
|
|
298
|
+
*/
|
|
299
|
+
export async function restoreOriginalState(cwd, originalBranch, wasStashed) {
|
|
300
|
+
// Switch back to original branch
|
|
301
|
+
await checkoutBranch(cwd, originalBranch);
|
|
302
|
+
// Pop stash if we stashed changes
|
|
303
|
+
if (wasStashed) {
|
|
304
|
+
await stashPop(cwd);
|
|
305
|
+
logger.info("git.restored_stash", { branch: originalBranch });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=branch-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-manager.js","sourceRoot":"","sources":["../../src/autofix/branch-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates conventional commit messages for autofix PRs.
|
|
5
|
+
*
|
|
6
|
+
* @module autofix/commit-generator
|
|
7
|
+
*/
|
|
8
|
+
import type { Severity } from "../certification/types.js";
|
|
9
|
+
import type { FixResult } from "../certification/autofix.js";
|
|
10
|
+
import type { AutofixCommit } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Generate a commit message for a batch of fixes
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateCommitMessage(fixes: FixResult[], severity?: Severity, options?: {
|
|
15
|
+
includeEmoji?: boolean;
|
|
16
|
+
includeSeverity?: boolean;
|
|
17
|
+
maxLength?: number;
|
|
18
|
+
}): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate extended commit body with details
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateCommitBody(fixes: FixResult[], severity?: Severity): string;
|
|
23
|
+
/**
|
|
24
|
+
* Create a commit with the staged changes
|
|
25
|
+
*/
|
|
26
|
+
export declare function createCommit(cwd: string, message: string, body?: string, options?: {
|
|
27
|
+
coAuthors?: string[];
|
|
28
|
+
signOff?: boolean;
|
|
29
|
+
}): Promise<AutofixCommit | {
|
|
30
|
+
error: string;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Generate PR title based on fixes
|
|
34
|
+
*/
|
|
35
|
+
export declare function generatePRTitle(fixes: FixResult[], severity?: Severity, template?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Generate PR body/description
|
|
38
|
+
*/
|
|
39
|
+
export declare function generatePRBody(fixes: FixResult[], severity?: Severity, options?: {
|
|
40
|
+
certificationId?: string;
|
|
41
|
+
includeBeforeAfter?: boolean;
|
|
42
|
+
}): string;
|
|
43
|
+
/**
|
|
44
|
+
* Categorize fixes by severity
|
|
45
|
+
*/
|
|
46
|
+
export declare function groupFixesBySeverity(fixes: FixResult[], findingSeverities: Map<string, Severity>): Map<Severity, FixResult[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Categorize fixes by file
|
|
49
|
+
*/
|
|
50
|
+
export declare function groupFixesByFile(fixes: FixResult[]): Map<string, FixResult[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Categorize fixes by pattern
|
|
53
|
+
*/
|
|
54
|
+
export declare function groupFixesByPattern(fixes: FixResult[]): Map<string, FixResult[]>;
|
|
55
|
+
//# sourceMappingURL=commit-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-generator.d.ts","sourceRoot":"","sources":["../../src/autofix/commit-generator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoChD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,EAAE,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE;IACR,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,MAAM,CAiER;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAAE,EAClB,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,CAmCR;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA,OAAO,CAAC,aAAa,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwD5C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAAE,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAiBR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,EAAE,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GACA,MAAM,CAwDR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,EAAE,EAClB,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACvC,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,CAW5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAU7E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAchF"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates conventional commit messages for autofix PRs.
|
|
5
|
+
*
|
|
6
|
+
* @module autofix/commit-generator
|
|
7
|
+
*/
|
|
8
|
+
import { git, getStagedFiles } from "./branch-manager.js";
|
|
9
|
+
import { logger } from "../logger.js";
|
|
10
|
+
/**
|
|
11
|
+
* Map severity to commit urgency indicators
|
|
12
|
+
*/
|
|
13
|
+
const SEVERITY_INDICATORS = {
|
|
14
|
+
critical: "🚨",
|
|
15
|
+
high: "⚠️",
|
|
16
|
+
medium: "🔧",
|
|
17
|
+
low: "📝",
|
|
18
|
+
info: "ℹ️",
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Map pattern categories to commit types
|
|
22
|
+
*/
|
|
23
|
+
function getCommitType(patternId) {
|
|
24
|
+
if (patternId.startsWith("sec-")) {
|
|
25
|
+
return "security";
|
|
26
|
+
}
|
|
27
|
+
if (patternId.startsWith("perf-")) {
|
|
28
|
+
return "perf";
|
|
29
|
+
}
|
|
30
|
+
if (patternId.startsWith("type-") || patternId.startsWith("qual-")) {
|
|
31
|
+
return "refactor";
|
|
32
|
+
}
|
|
33
|
+
return "fix";
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate a commit message for a batch of fixes
|
|
37
|
+
*/
|
|
38
|
+
export function generateCommitMessage(fixes, severity, options) {
|
|
39
|
+
const { includeEmoji = false, includeSeverity = true, maxLength = 72, } = options ?? {};
|
|
40
|
+
const appliedFixes = fixes.filter((f) => f.applied || f.diff);
|
|
41
|
+
if (appliedFixes.length === 0) {
|
|
42
|
+
return "chore: attempted autofix (no changes applied)";
|
|
43
|
+
}
|
|
44
|
+
// Group by pattern type
|
|
45
|
+
const patterns = new Map();
|
|
46
|
+
const files = new Set();
|
|
47
|
+
for (const fix of appliedFixes) {
|
|
48
|
+
files.add(fix.file);
|
|
49
|
+
// Extract pattern from findingId (e.g., "sec-001" -> "sec")
|
|
50
|
+
const parts = fix.findingId.split("-");
|
|
51
|
+
if (parts.length >= 1) {
|
|
52
|
+
const prefix = parts[0];
|
|
53
|
+
patterns.set(prefix, (patterns.get(prefix) || 0) + 1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Determine primary commit type
|
|
57
|
+
const patternEntries = Array.from(patterns.entries());
|
|
58
|
+
const primaryPattern = patternEntries.sort((a, b) => b[1] - a[1])[0]?.[0] ?? "fix";
|
|
59
|
+
const commitType = primaryPattern === "sec" ? "security" : "fix";
|
|
60
|
+
// Build message parts
|
|
61
|
+
const parts = [];
|
|
62
|
+
// Emoji prefix
|
|
63
|
+
if (includeEmoji && severity) {
|
|
64
|
+
parts.push(SEVERITY_INDICATORS[severity]);
|
|
65
|
+
}
|
|
66
|
+
// Commit type and scope
|
|
67
|
+
const scope = commitType === "security" ? "security" : undefined;
|
|
68
|
+
const typeScope = scope ? `fix(${scope})` : "fix";
|
|
69
|
+
parts.push(`${typeScope}:`);
|
|
70
|
+
// Summary
|
|
71
|
+
if (appliedFixes.length === 1) {
|
|
72
|
+
parts.push(`Apply autofix for ${appliedFixes[0].findingId}`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
parts.push(`Apply ${appliedFixes.length} automated fixes`);
|
|
76
|
+
}
|
|
77
|
+
// Severity tag
|
|
78
|
+
if (includeSeverity && severity) {
|
|
79
|
+
parts.push(`[${severity}]`);
|
|
80
|
+
}
|
|
81
|
+
let message = parts.join(" ");
|
|
82
|
+
// Truncate if needed
|
|
83
|
+
if (message.length > maxLength) {
|
|
84
|
+
message = message.slice(0, maxLength - 3) + "...";
|
|
85
|
+
}
|
|
86
|
+
return message;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Generate extended commit body with details
|
|
90
|
+
*/
|
|
91
|
+
export function generateCommitBody(fixes, severity) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
const appliedFixes = fixes.filter((f) => f.applied || f.diff);
|
|
94
|
+
if (appliedFixes.length === 0) {
|
|
95
|
+
return "";
|
|
96
|
+
}
|
|
97
|
+
// Summary
|
|
98
|
+
lines.push("## Automated Security Fixes\n");
|
|
99
|
+
if (severity) {
|
|
100
|
+
lines.push(`Severity: **${severity.toUpperCase()}**\n`);
|
|
101
|
+
}
|
|
102
|
+
// Group fixes by file
|
|
103
|
+
const byFile = new Map();
|
|
104
|
+
for (const fix of appliedFixes) {
|
|
105
|
+
const existing = byFile.get(fix.file) || [];
|
|
106
|
+
existing.push(fix);
|
|
107
|
+
byFile.set(fix.file, existing);
|
|
108
|
+
}
|
|
109
|
+
lines.push("### Changes\n");
|
|
110
|
+
for (const [file, fileFixes] of byFile) {
|
|
111
|
+
lines.push(`- **${file}**`);
|
|
112
|
+
for (const fix of fileFixes) {
|
|
113
|
+
lines.push(` - ${fix.findingId}: Line ${fix.diff?.lineNumber || "unknown"}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push("\n---");
|
|
117
|
+
lines.push("Generated by Vaspera Hardening MCP");
|
|
118
|
+
return lines.join("\n");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create a commit with the staged changes
|
|
122
|
+
*/
|
|
123
|
+
export async function createCommit(cwd, message, body, options) {
|
|
124
|
+
const stagedFiles = await getStagedFiles(cwd);
|
|
125
|
+
if (stagedFiles.length === 0) {
|
|
126
|
+
return { error: "No staged files to commit" };
|
|
127
|
+
}
|
|
128
|
+
// Build full commit message
|
|
129
|
+
const fullMessage = body ? `${message}\n\n${body}` : message;
|
|
130
|
+
// Add co-authors
|
|
131
|
+
let finalMessage = fullMessage;
|
|
132
|
+
const coAuthors = options?.coAuthors ?? ["Claude <noreply@anthropic.com>"];
|
|
133
|
+
if (coAuthors.length > 0) {
|
|
134
|
+
const coAuthorLines = coAuthors
|
|
135
|
+
.map((author) => `Co-Authored-By: ${author}`)
|
|
136
|
+
.join("\n");
|
|
137
|
+
finalMessage = `${finalMessage}\n\n${coAuthorLines}`;
|
|
138
|
+
}
|
|
139
|
+
// Build commit args
|
|
140
|
+
const args = ["commit", "-m", finalMessage];
|
|
141
|
+
if (options?.signOff) {
|
|
142
|
+
args.push("--signoff");
|
|
143
|
+
}
|
|
144
|
+
logger.info("git.commit", {
|
|
145
|
+
files: stagedFiles.length,
|
|
146
|
+
messagePreview: message.slice(0, 50),
|
|
147
|
+
});
|
|
148
|
+
const result = await git(args, { cwd });
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
logger.error("git.commit_failed", {
|
|
151
|
+
error: result.stderr,
|
|
152
|
+
exitCode: result.exitCode,
|
|
153
|
+
});
|
|
154
|
+
return { error: result.stderr || "Commit failed" };
|
|
155
|
+
}
|
|
156
|
+
// Get the commit SHA
|
|
157
|
+
const shaResult = await git(["rev-parse", "HEAD"], { cwd });
|
|
158
|
+
const sha = shaResult.success ? shaResult.stdout : undefined;
|
|
159
|
+
logger.info("git.commit_success", { sha, files: stagedFiles.length });
|
|
160
|
+
return {
|
|
161
|
+
message,
|
|
162
|
+
sha,
|
|
163
|
+
files: stagedFiles,
|
|
164
|
+
author: "vaspera-hardening",
|
|
165
|
+
coAuthors,
|
|
166
|
+
signed: false,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Generate PR title based on fixes
|
|
171
|
+
*/
|
|
172
|
+
export function generatePRTitle(fixes, severity, template) {
|
|
173
|
+
const appliedFixes = fixes.filter((f) => f.applied || f.diff);
|
|
174
|
+
const count = appliedFixes.length;
|
|
175
|
+
const files = new Set(appliedFixes.map((f) => f.file)).size;
|
|
176
|
+
if (template) {
|
|
177
|
+
return template
|
|
178
|
+
.replace("{{count}}", String(count))
|
|
179
|
+
.replace("{{severity}}", severity ?? "mixed")
|
|
180
|
+
.replace("{{files}}", String(files));
|
|
181
|
+
}
|
|
182
|
+
if (severity) {
|
|
183
|
+
return `fix(security): Apply ${count} ${severity}-severity autofix${count !== 1 ? "es" : ""}`;
|
|
184
|
+
}
|
|
185
|
+
return `fix(security): Apply ${count} automated security fix${count !== 1 ? "es" : ""}`;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Generate PR body/description
|
|
189
|
+
*/
|
|
190
|
+
export function generatePRBody(fixes, severity, options) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
const appliedFixes = fixes.filter((f) => f.applied || f.diff);
|
|
193
|
+
lines.push("## Summary\n");
|
|
194
|
+
lines.push("This PR contains automated security fixes generated by Vaspera Hardening MCP.\n");
|
|
195
|
+
if (severity) {
|
|
196
|
+
lines.push(`**Severity Level:** ${severity.toUpperCase()}\n`);
|
|
197
|
+
}
|
|
198
|
+
if (options?.certificationId) {
|
|
199
|
+
lines.push(`**Certification ID:** \`${options.certificationId}\`\n`);
|
|
200
|
+
}
|
|
201
|
+
lines.push("## Fixes Applied\n");
|
|
202
|
+
// Group by file
|
|
203
|
+
const byFile = new Map();
|
|
204
|
+
for (const fix of appliedFixes) {
|
|
205
|
+
const existing = byFile.get(fix.file) || [];
|
|
206
|
+
existing.push(fix);
|
|
207
|
+
byFile.set(fix.file, existing);
|
|
208
|
+
}
|
|
209
|
+
for (const [file, fileFixes] of byFile) {
|
|
210
|
+
lines.push(`### \`${file}\`\n`);
|
|
211
|
+
for (const fix of fileFixes) {
|
|
212
|
+
lines.push(`- **${fix.findingId}** (line ${fix.diff?.lineNumber || "?"})`);
|
|
213
|
+
if (options?.includeBeforeAfter && fix.diff) {
|
|
214
|
+
lines.push("");
|
|
215
|
+
lines.push("<details>");
|
|
216
|
+
lines.push("<summary>View changes</summary>\n");
|
|
217
|
+
lines.push("**Before:**");
|
|
218
|
+
lines.push("```");
|
|
219
|
+
lines.push(fix.diff.before);
|
|
220
|
+
lines.push("```\n");
|
|
221
|
+
lines.push("**After:**");
|
|
222
|
+
lines.push("```");
|
|
223
|
+
lines.push(fix.diff.after);
|
|
224
|
+
lines.push("```");
|
|
225
|
+
lines.push("</details>\n");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
lines.push("\n## Review Checklist\n");
|
|
230
|
+
lines.push("- [ ] Changes are semantically correct");
|
|
231
|
+
lines.push("- [ ] No regressions introduced");
|
|
232
|
+
lines.push("- [ ] Tests pass");
|
|
233
|
+
lines.push("\n---");
|
|
234
|
+
lines.push("🤖 Generated with [Vaspera Hardening MCP](https://github.com/RCOLKITT/hardening-mcp)");
|
|
235
|
+
return lines.join("\n");
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Categorize fixes by severity
|
|
239
|
+
*/
|
|
240
|
+
export function groupFixesBySeverity(fixes, findingSeverities) {
|
|
241
|
+
const groups = new Map();
|
|
242
|
+
for (const fix of fixes) {
|
|
243
|
+
const severity = findingSeverities.get(fix.findingId) ?? "medium";
|
|
244
|
+
const existing = groups.get(severity) || [];
|
|
245
|
+
existing.push(fix);
|
|
246
|
+
groups.set(severity, existing);
|
|
247
|
+
}
|
|
248
|
+
return groups;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Categorize fixes by file
|
|
252
|
+
*/
|
|
253
|
+
export function groupFixesByFile(fixes) {
|
|
254
|
+
const groups = new Map();
|
|
255
|
+
for (const fix of fixes) {
|
|
256
|
+
const existing = groups.get(fix.file) || [];
|
|
257
|
+
existing.push(fix);
|
|
258
|
+
groups.set(fix.file, existing);
|
|
259
|
+
}
|
|
260
|
+
return groups;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Categorize fixes by pattern
|
|
264
|
+
*/
|
|
265
|
+
export function groupFixesByPattern(fixes) {
|
|
266
|
+
const groups = new Map();
|
|
267
|
+
for (const fix of fixes) {
|
|
268
|
+
// Extract pattern from findingId (e.g., "sec-hardcoded-secret-001" -> "sec-hardcoded-secret")
|
|
269
|
+
const parts = fix.findingId.split("-");
|
|
270
|
+
const patternId = parts.slice(0, -1).join("-") || fix.findingId;
|
|
271
|
+
const existing = groups.get(patternId) || [];
|
|
272
|
+
existing.push(fix);
|
|
273
|
+
groups.set(patternId, existing);
|
|
274
|
+
}
|
|
275
|
+
return groups;
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=commit-generator.js.map
|