@autopilot-harness/core 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/LICENSE +21 -0
- package/README.md +8 -0
- package/dist/autopilot-ignore.d.ts +23 -0
- package/dist/autopilot-ignore.d.ts.map +1 -0
- package/dist/autopilot-ignore.js +271 -0
- package/dist/autopilot-ignore.js.map +1 -0
- package/dist/checklist-md.d.ts +62 -0
- package/dist/checklist-md.d.ts.map +1 -0
- package/dist/checklist-md.js +217 -0
- package/dist/checklist-md.js.map +1 -0
- package/dist/code-edit-detector.d.ts +10 -0
- package/dist/code-edit-detector.d.ts.map +1 -0
- package/dist/code-edit-detector.js +44 -0
- package/dist/code-edit-detector.js.map +1 -0
- package/dist/i18n-render.d.ts +3 -0
- package/dist/i18n-render.d.ts.map +1 -0
- package/dist/i18n-render.js +8 -0
- package/dist/i18n-render.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/list-tracks.d.ts +29 -0
- package/dist/list-tracks.d.ts.map +1 -0
- package/dist/list-tracks.js +232 -0
- package/dist/list-tracks.js.map +1 -0
- package/dist/migrate.d.ts +19 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +101 -0
- package/dist/migrate.js.map +1 -0
- package/dist/phase-actions.d.ts +35 -0
- package/dist/phase-actions.d.ts.map +1 -0
- package/dist/phase-actions.js +480 -0
- package/dist/phase-actions.js.map +1 -0
- package/dist/project-config.d.ts +29 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +301 -0
- package/dist/project-config.js.map +1 -0
- package/dist/project-path.d.ts +26 -0
- package/dist/project-path.d.ts.map +1 -0
- package/dist/project-path.js +104 -0
- package/dist/project-path.js.map +1 -0
- package/dist/review-engine.d.ts +263 -0
- package/dist/review-engine.d.ts.map +1 -0
- package/dist/review-engine.js +2273 -0
- package/dist/review-engine.js.map +1 -0
- package/dist/review-i18n.d.ts +29 -0
- package/dist/review-i18n.d.ts.map +1 -0
- package/dist/review-i18n.js +54 -0
- package/dist/review-i18n.js.map +1 -0
- package/dist/review-lenses.d.ts +13 -0
- package/dist/review-lenses.d.ts.map +1 -0
- package/dist/review-lenses.js +43 -0
- package/dist/review-lenses.js.map +1 -0
- package/dist/review-runtime.d.ts +12 -0
- package/dist/review-runtime.d.ts.map +1 -0
- package/dist/review-runtime.js +37 -0
- package/dist/review-runtime.js.map +1 -0
- package/dist/review-scope.d.ts +9 -0
- package/dist/review-scope.d.ts.map +1 -0
- package/dist/review-scope.js +92 -0
- package/dist/review-scope.js.map +1 -0
- package/dist/sqlite.d.ts +15 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +38 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/state-store.d.ts +277 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +1091 -0
- package/dist/state-store.js.map +1 -0
- package/dist/track-slug.d.ts +4 -0
- package/dist/track-slug.d.ts.map +1 -0
- package/dist/track-slug.js +18 -0
- package/dist/track-slug.js.map +1 -0
- package/dist/transcript-followup.d.ts +59 -0
- package/dist/transcript-followup.d.ts.map +1 -0
- package/dist/transcript-followup.js +310 -0
- package/dist/transcript-followup.js.map +1 -0
- package/dist/trigger-parser.d.ts +54 -0
- package/dist/trigger-parser.d.ts.map +1 -0
- package/dist/trigger-parser.js +250 -0
- package/dist/trigger-parser.js.map +1 -0
- package/dist/verify-report.d.ts +52 -0
- package/dist/verify-report.d.ts.map +1 -0
- package/dist/verify-report.js +195 -0
- package/dist/verify-report.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isRealpathInsideProject, normalizeProjectRoot } from "./project-path.js";
|
|
4
|
+
const MAX_CONFIG_BYTES = 1_000_000;
|
|
5
|
+
export const DEFAULT_PROJECT_REVIEW_CONFIG = {
|
|
6
|
+
confirmRounds: 5,
|
|
7
|
+
reviewScope: "executing_only",
|
|
8
|
+
verifyEnabled: false,
|
|
9
|
+
// freeze: chặn mutate hằng số mặc định làm bẩn mọi clone sau này
|
|
10
|
+
verifyCommands: Object.freeze([]),
|
|
11
|
+
maxIdleStops: 5,
|
|
12
|
+
maxErrorsBeforePause: 0,
|
|
13
|
+
locale: "en",
|
|
14
|
+
};
|
|
15
|
+
/** Bản sao độc lập — tránh chia sẻ mảng verifyCommands giữa các lần gọi. */
|
|
16
|
+
function parseReviewScope(raw) {
|
|
17
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
18
|
+
if (s === "project" || s === "always" || s === "all")
|
|
19
|
+
return "project";
|
|
20
|
+
return "executing_only";
|
|
21
|
+
}
|
|
22
|
+
function cloneDefaultProjectReviewConfig() {
|
|
23
|
+
return {
|
|
24
|
+
confirmRounds: DEFAULT_PROJECT_REVIEW_CONFIG.confirmRounds,
|
|
25
|
+
reviewScope: DEFAULT_PROJECT_REVIEW_CONFIG.reviewScope,
|
|
26
|
+
verifyEnabled: DEFAULT_PROJECT_REVIEW_CONFIG.verifyEnabled,
|
|
27
|
+
verifyCommands: [],
|
|
28
|
+
maxIdleStops: DEFAULT_PROJECT_REVIEW_CONFIG.maxIdleStops,
|
|
29
|
+
maxErrorsBeforePause: DEFAULT_PROJECT_REVIEW_CONFIG.maxErrorsBeforePause,
|
|
30
|
+
locale: DEFAULT_PROJECT_REVIEW_CONFIG.locale,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function coerceIntInRange(raw, min, max, fallback) {
|
|
34
|
+
if (raw == null || !raw.trim())
|
|
35
|
+
return fallback;
|
|
36
|
+
const n = Number(raw.trim());
|
|
37
|
+
// Non-integer / below min → default. Above max → clamp (e.g. max_before_pause
|
|
38
|
+
// 1001 must not fail-open to 0/unlimited and disable the pause gate).
|
|
39
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < min)
|
|
40
|
+
return fallback;
|
|
41
|
+
if (n > max)
|
|
42
|
+
return max;
|
|
43
|
+
return n;
|
|
44
|
+
}
|
|
45
|
+
function unquote(value) {
|
|
46
|
+
const v = value.trim();
|
|
47
|
+
if ((v.startsWith('"') && v.endsWith('"')) ||
|
|
48
|
+
(v.startsWith("'") && v.endsWith("'"))) {
|
|
49
|
+
return v.slice(1, -1);
|
|
50
|
+
}
|
|
51
|
+
return v;
|
|
52
|
+
}
|
|
53
|
+
function isPlainObject(value) {
|
|
54
|
+
return (!!value &&
|
|
55
|
+
typeof value === "object" &&
|
|
56
|
+
!Array.isArray(value) &&
|
|
57
|
+
Object.prototype.toString.call(value) === "[object Object]");
|
|
58
|
+
}
|
|
59
|
+
/** Keys that must never be written from YAML (match config-merge policy). */
|
|
60
|
+
function isUnsafeKey(key) {
|
|
61
|
+
return key === "__proto__" || key === "prototype" || key === "constructor";
|
|
62
|
+
}
|
|
63
|
+
function coerceScalar(value) {
|
|
64
|
+
if (value === "true")
|
|
65
|
+
return true;
|
|
66
|
+
if (value === "false")
|
|
67
|
+
return false;
|
|
68
|
+
if (value === "null" || value === "~")
|
|
69
|
+
return null;
|
|
70
|
+
return unquote(value);
|
|
71
|
+
}
|
|
72
|
+
function lineIndent(line) {
|
|
73
|
+
let n = 0;
|
|
74
|
+
for (let i = 0; i < line.length; i++) {
|
|
75
|
+
const ch = line[i];
|
|
76
|
+
if (ch === " ")
|
|
77
|
+
n += 1;
|
|
78
|
+
else if (ch === "\t")
|
|
79
|
+
n += 2;
|
|
80
|
+
else
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return n;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Minimal indentation-aware YAML subset reader for Autopilot config.
|
|
87
|
+
* Avoids bundling the full `yaml` package into the Cursor hook vendor ESM.
|
|
88
|
+
*/
|
|
89
|
+
function parseSimpleYaml(raw) {
|
|
90
|
+
const root = {};
|
|
91
|
+
const stack = [{ indent: -1, obj: root }];
|
|
92
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
93
|
+
if (!line.trim() || line.trim().startsWith("#"))
|
|
94
|
+
continue;
|
|
95
|
+
const indent = lineIndent(line);
|
|
96
|
+
const trimmed = line.trim();
|
|
97
|
+
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
98
|
+
stack.pop();
|
|
99
|
+
}
|
|
100
|
+
const frame = stack[stack.length - 1];
|
|
101
|
+
if (trimmed.startsWith("- ")) {
|
|
102
|
+
const itemRaw = trimmed.slice(2).trim();
|
|
103
|
+
if (!frame.openKey)
|
|
104
|
+
continue;
|
|
105
|
+
let list = frame.obj[frame.openKey];
|
|
106
|
+
if (!Array.isArray(list)) {
|
|
107
|
+
list = [];
|
|
108
|
+
frame.obj[frame.openKey] = list;
|
|
109
|
+
}
|
|
110
|
+
if (itemRaw.includes(":") && !itemRaw.startsWith("{")) {
|
|
111
|
+
const item = {};
|
|
112
|
+
list.push(item);
|
|
113
|
+
const m = itemRaw.match(/^([^:#]+):\s*(.*)$/);
|
|
114
|
+
if (m) {
|
|
115
|
+
const k = m[1].trim();
|
|
116
|
+
const v = m[2].trim();
|
|
117
|
+
if (!isUnsafeKey(k)) {
|
|
118
|
+
item[k] = v === "" ? null : coerceScalar(v);
|
|
119
|
+
}
|
|
120
|
+
stack.push({ indent, obj: item });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
list.push(coerceScalar(itemRaw));
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const kv = trimmed.match(/^([^:#]+):\s*(.*)$/);
|
|
129
|
+
if (!kv)
|
|
130
|
+
continue;
|
|
131
|
+
const key = kv[1].trim();
|
|
132
|
+
const value = kv[2].trim();
|
|
133
|
+
if (isUnsafeKey(key))
|
|
134
|
+
continue;
|
|
135
|
+
// Nested content under an openKey → materialize mapping and descend.
|
|
136
|
+
if (frame.openKey &&
|
|
137
|
+
frame.openKeyIndent != null &&
|
|
138
|
+
indent > frame.openKeyIndent) {
|
|
139
|
+
let child = frame.obj[frame.openKey];
|
|
140
|
+
if (!isPlainObject(child) || Array.isArray(child)) {
|
|
141
|
+
child = {};
|
|
142
|
+
frame.obj[frame.openKey] = child;
|
|
143
|
+
}
|
|
144
|
+
const childObj = child;
|
|
145
|
+
const childIndent = frame.openKeyIndent;
|
|
146
|
+
frame.openKey = undefined;
|
|
147
|
+
frame.openKeyIndent = undefined;
|
|
148
|
+
stack.push({ indent: childIndent, obj: childObj });
|
|
149
|
+
const childFrame = stack[stack.length - 1];
|
|
150
|
+
if (value === "" || value === "|" || value === ">") {
|
|
151
|
+
childFrame.openKey = key;
|
|
152
|
+
childFrame.openKeyIndent = indent;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
childObj[key] = coerceScalar(value);
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (value === "" || value === "|" || value === ">") {
|
|
160
|
+
frame.openKey = key;
|
|
161
|
+
frame.openKeyIndent = indent;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
frame.openKey = undefined;
|
|
165
|
+
frame.openKeyIndent = undefined;
|
|
166
|
+
frame.obj[key] = coerceScalar(value);
|
|
167
|
+
}
|
|
168
|
+
return root;
|
|
169
|
+
}
|
|
170
|
+
function coerceBool(raw) {
|
|
171
|
+
if (raw === true || raw === "true")
|
|
172
|
+
return true;
|
|
173
|
+
if (raw === false || raw === "false")
|
|
174
|
+
return false;
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
function parseVerifyCommands(raw) {
|
|
178
|
+
if (!Array.isArray(raw))
|
|
179
|
+
return [];
|
|
180
|
+
const out = [];
|
|
181
|
+
for (const entry of raw) {
|
|
182
|
+
if (!isPlainObject(entry))
|
|
183
|
+
continue;
|
|
184
|
+
if (typeof entry.id !== "string" || !entry.id.trim())
|
|
185
|
+
continue;
|
|
186
|
+
const cmd = { id: entry.id.trim() };
|
|
187
|
+
if (typeof entry.run === "string")
|
|
188
|
+
cmd.run = entry.run;
|
|
189
|
+
const required = coerceBool(entry.required);
|
|
190
|
+
if (required !== undefined)
|
|
191
|
+
cmd.required = required;
|
|
192
|
+
out.push(cmd);
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Load review runtime settings from `.autopilot/config.yml`.
|
|
198
|
+
* Missing / unreadable / corrupt → safe defaults (hook fail-open).
|
|
199
|
+
*/
|
|
200
|
+
export function loadProjectReviewConfig(projectRoot) {
|
|
201
|
+
// Fail closed on unusable roots before any open (empty/blank/NUL → cwd-relative join).
|
|
202
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
203
|
+
if (!root) {
|
|
204
|
+
return cloneDefaultProjectReviewConfig();
|
|
205
|
+
}
|
|
206
|
+
const configPath = path.join(root, ".autopilot", "config.yml");
|
|
207
|
+
try {
|
|
208
|
+
const nofollow = typeof fs.constants.O_NOFOLLOW === "number" ? fs.constants.O_NOFOLLOW : 0;
|
|
209
|
+
// Without O_NOFOLLOW: lstat before open — refuse leaf symlink follow.
|
|
210
|
+
if (nofollow === 0) {
|
|
211
|
+
if (!fs.existsSync(configPath))
|
|
212
|
+
return cloneDefaultProjectReviewConfig();
|
|
213
|
+
if (fs.lstatSync(configPath).isSymbolicLink()) {
|
|
214
|
+
return cloneDefaultProjectReviewConfig();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
let fd;
|
|
218
|
+
try {
|
|
219
|
+
// O_NOFOLLOW + fstat: block leaf-symlink TOCTOU and oversized reads after stat.
|
|
220
|
+
fd = fs.openSync(configPath, fs.constants.O_RDONLY | nofollow);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return cloneDefaultProjectReviewConfig();
|
|
224
|
+
}
|
|
225
|
+
let raw;
|
|
226
|
+
try {
|
|
227
|
+
const st = fs.fstatSync(fd);
|
|
228
|
+
if (!st.isFile() || st.size > MAX_CONFIG_BYTES) {
|
|
229
|
+
return cloneDefaultProjectReviewConfig();
|
|
230
|
+
}
|
|
231
|
+
// Bind fd to path identity always (intermediate-dir swap-back TOCTOU).
|
|
232
|
+
const lst = fs.lstatSync(configPath);
|
|
233
|
+
if (lst.isSymbolicLink() || !lst.isFile()) {
|
|
234
|
+
return cloneDefaultProjectReviewConfig();
|
|
235
|
+
}
|
|
236
|
+
if (lst.ino !== st.ino || lst.dev !== st.dev) {
|
|
237
|
+
return cloneDefaultProjectReviewConfig();
|
|
238
|
+
}
|
|
239
|
+
if (!isRealpathInsideProject(root, configPath)) {
|
|
240
|
+
return cloneDefaultProjectReviewConfig();
|
|
241
|
+
}
|
|
242
|
+
// Read exactly the fstat size — avoid OOM if the file grows after fstat.
|
|
243
|
+
const buf = Buffer.alloc(st.size);
|
|
244
|
+
const n = fs.readSync(fd, buf, 0, st.size, 0);
|
|
245
|
+
raw = buf.subarray(0, n).toString("utf8");
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
fs.closeSync(fd);
|
|
249
|
+
}
|
|
250
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_CONFIG_BYTES) {
|
|
251
|
+
return cloneDefaultProjectReviewConfig();
|
|
252
|
+
}
|
|
253
|
+
// Strip UTF-8 BOM so the first key is not shadowed as "\uFEFFlocale".
|
|
254
|
+
const text = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
255
|
+
const parsed = parseSimpleYaml(text);
|
|
256
|
+
if (!isPlainObject(parsed))
|
|
257
|
+
return cloneDefaultProjectReviewConfig();
|
|
258
|
+
const review = isPlainObject(parsed.review) ? parsed.review : {};
|
|
259
|
+
const verify = isPlainObject(review.verify) ? review.verify : {};
|
|
260
|
+
const stuck = isPlainObject(review.stuck) ? review.stuck : {};
|
|
261
|
+
const errors = isPlainObject(review.errors) ? review.errors : {};
|
|
262
|
+
// Một đường normalize — tránh load vs normalize lệch kẹp biên / bool.
|
|
263
|
+
return normalizeProjectReviewConfig({
|
|
264
|
+
confirmRounds: review.confirm_rounds,
|
|
265
|
+
reviewScope: review.scope,
|
|
266
|
+
verifyEnabled: verify.enabled,
|
|
267
|
+
verifyCommands: verify.commands,
|
|
268
|
+
maxIdleStops: stuck.max_idle_stops,
|
|
269
|
+
maxErrorsBeforePause: errors.max_before_pause,
|
|
270
|
+
locale: parsed.locale,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return cloneDefaultProjectReviewConfig();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Kẹp biên + làm sạch — mọi preloaded/config đều phải qua đây
|
|
279
|
+
* (không cho bypass confirm_rounds / idle / commands).
|
|
280
|
+
*/
|
|
281
|
+
export function normalizeProjectReviewConfig(raw) {
|
|
282
|
+
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
283
|
+
return cloneDefaultProjectReviewConfig();
|
|
284
|
+
}
|
|
285
|
+
const o = raw;
|
|
286
|
+
return {
|
|
287
|
+
confirmRounds: coerceIntInRange(o.confirmRounds != null ? String(o.confirmRounds) : undefined, 1, 5, DEFAULT_PROJECT_REVIEW_CONFIG.confirmRounds),
|
|
288
|
+
reviewScope: parseReviewScope(o.reviewScope),
|
|
289
|
+
verifyEnabled: coerceBool(o.verifyEnabled) === true,
|
|
290
|
+
verifyCommands: parseVerifyCommands(o.verifyCommands),
|
|
291
|
+
maxIdleStops: coerceIntInRange(o.maxIdleStops != null ? String(o.maxIdleStops) : undefined, 1, 100, DEFAULT_PROJECT_REVIEW_CONFIG.maxIdleStops),
|
|
292
|
+
// 0 = unlimited; clamp 0..1000 (invalid → default unlimited)
|
|
293
|
+
maxErrorsBeforePause: coerceIntInRange(o.maxErrorsBeforePause != null
|
|
294
|
+
? String(o.maxErrorsBeforePause)
|
|
295
|
+
: undefined, 0, 1000, DEFAULT_PROJECT_REVIEW_CONFIG.maxErrorsBeforePause),
|
|
296
|
+
locale: typeof o.locale === "string" && o.locale.trim()
|
|
297
|
+
? o.locale.trim()
|
|
298
|
+
: DEFAULT_PROJECT_REVIEW_CONFIG.locale,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=project-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-config.js","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize untrusted projectRoot for join/resolve/containment.
|
|
3
|
+
* Leading/trailing spaces make absolute roots look relative to path.resolve
|
|
4
|
+
* (e.g. " /tmp/p " → cwd-relative) — always trim before FS use.
|
|
5
|
+
* Returns null for non-string, empty/blank, or NUL-poisoned roots.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeProjectRoot(projectRoot: string): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve both paths and require target under project root.
|
|
10
|
+
* Catches intermediate directory symlink escapes that O_NOFOLLOW on the leaf cannot.
|
|
11
|
+
* Relative targetPath is resolved against projectRoot (not process.cwd()).
|
|
12
|
+
* projectRoot is normalized (trim); targetPath is not trimmed (filenames may differ),
|
|
13
|
+
* but blank-only targetPath is refused.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isRealpathInsideProject(projectRoot: string, targetPath: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Lexical containment (no symlink resolve) — for paths that may not exist yet.
|
|
18
|
+
* Relative targets resolve against normalized projectRoot (targetPath not trimmed).
|
|
19
|
+
*/
|
|
20
|
+
export declare function isLexicallyInsideProject(projectRoot: string, targetPath: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Normalize untrusted plansDir under projectRoot.
|
|
23
|
+
* Defaults blank/undefined to "plans". Returns null for absolute/~ / .. / bad chars.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeInProjectPlansDir(projectRoot: string, plansDir?: string | null): string | null;
|
|
26
|
+
//# sourceMappingURL=project-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-path.d.ts","sourceRoot":"","sources":["../src/project-path.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMvE;AAaD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAkBT;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAiBT;AAKD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GACvB,MAAM,GAAG,IAAI,CAuBf"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Normalize untrusted projectRoot for join/resolve/containment.
|
|
5
|
+
* Leading/trailing spaces make absolute roots look relative to path.resolve
|
|
6
|
+
* (e.g. " /tmp/p " → cwd-relative) — always trim before FS use.
|
|
7
|
+
* Returns null for non-string, empty/blank, or NUL-poisoned roots.
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeProjectRoot(projectRoot) {
|
|
10
|
+
if (typeof projectRoot !== "string" || projectRoot.includes("\0")) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const root = projectRoot.trim();
|
|
14
|
+
return root || null;
|
|
15
|
+
}
|
|
16
|
+
/** True when realTarget is realRoot or a descendant (not a sibling escape). */
|
|
17
|
+
function isRealpathInsideRoot(realRoot, realTarget) {
|
|
18
|
+
if (realTarget === realRoot)
|
|
19
|
+
return true;
|
|
20
|
+
const rel = path.relative(realRoot, realTarget);
|
|
21
|
+
return !(rel === ".." ||
|
|
22
|
+
rel.startsWith(`..${path.sep}`) ||
|
|
23
|
+
path.isAbsolute(rel));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve both paths and require target under project root.
|
|
27
|
+
* Catches intermediate directory symlink escapes that O_NOFOLLOW on the leaf cannot.
|
|
28
|
+
* Relative targetPath is resolved against projectRoot (not process.cwd()).
|
|
29
|
+
* projectRoot is normalized (trim); targetPath is not trimmed (filenames may differ),
|
|
30
|
+
* but blank-only targetPath is refused.
|
|
31
|
+
*/
|
|
32
|
+
export function isRealpathInsideProject(projectRoot, targetPath) {
|
|
33
|
+
if (typeof targetPath !== "string" ||
|
|
34
|
+
targetPath.includes("\0") ||
|
|
35
|
+
!targetPath.trim()) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
39
|
+
if (!root)
|
|
40
|
+
return false;
|
|
41
|
+
try {
|
|
42
|
+
const realRoot = fs.realpathSync(root);
|
|
43
|
+
// path.resolve(root, abs) → abs; path.resolve(root, rel) → root/rel (not cwd).
|
|
44
|
+
const realTarget = fs.realpathSync(path.resolve(root, targetPath));
|
|
45
|
+
return isRealpathInsideRoot(realRoot, realTarget);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Lexical containment (no symlink resolve) — for paths that may not exist yet.
|
|
53
|
+
* Relative targets resolve against normalized projectRoot (targetPath not trimmed).
|
|
54
|
+
*/
|
|
55
|
+
export function isLexicallyInsideProject(projectRoot, targetPath) {
|
|
56
|
+
if (typeof targetPath !== "string" ||
|
|
57
|
+
targetPath.includes("\0") ||
|
|
58
|
+
!targetPath.trim()) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
62
|
+
if (!root)
|
|
63
|
+
return false;
|
|
64
|
+
try {
|
|
65
|
+
const absRoot = path.resolve(root);
|
|
66
|
+
const abs = path.resolve(root, targetPath);
|
|
67
|
+
return isRealpathInsideRoot(absRoot, abs);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Align with CLI normalizePlansDir — relative, no traversal, safe path chars. */
|
|
74
|
+
const PLANS_DIR_RE = /^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/;
|
|
75
|
+
/**
|
|
76
|
+
* Normalize untrusted plansDir under projectRoot.
|
|
77
|
+
* Defaults blank/undefined to "plans". Returns null for absolute/~ / .. / bad chars.
|
|
78
|
+
*/
|
|
79
|
+
export function normalizeInProjectPlansDir(projectRoot, plansDir) {
|
|
80
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
81
|
+
if (!root)
|
|
82
|
+
return null;
|
|
83
|
+
let raw = typeof plansDir === "string" && plansDir.trim() ? plansDir.trim() : "plans";
|
|
84
|
+
raw = raw.replace(/\/+$/, "") || "plans";
|
|
85
|
+
if (path.isAbsolute(raw) ||
|
|
86
|
+
raw.startsWith("~") ||
|
|
87
|
+
raw.includes("\0") ||
|
|
88
|
+
raw.includes("\n") ||
|
|
89
|
+
raw.includes("\r") ||
|
|
90
|
+
raw.includes("\\")) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const parts = raw.split("/");
|
|
94
|
+
if (parts.some((p) => p === "" || p === "." || p === ".."))
|
|
95
|
+
return null;
|
|
96
|
+
const rel = parts.join("/");
|
|
97
|
+
if (!PLANS_DIR_RE.test(rel))
|
|
98
|
+
return null;
|
|
99
|
+
const abs = path.resolve(root, rel);
|
|
100
|
+
if (!isLexicallyInsideProject(root, abs))
|
|
101
|
+
return null;
|
|
102
|
+
return rel;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=project-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-path.js","sourceRoot":"","sources":["../src/project-path.ts"],"names":[],"mappings":"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"}
|