pi-codex-marketplace 0.6.0 → 0.6.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-codex-marketplace",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Bridge Package for Codex and Claude Marketplace compatibility in Pi — 極簡 /codex-marketplace 純文字指令(add/list/install/update/disable/enable/remove/forget)、單一 Global Bridge State、當下最新安裝與即時投影",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -25,10 +25,10 @@ export function containedPathSyntax(p: string): PathSyntax {
|
|
|
25
25
|
if (p.includes('\\')) return { ok: false, reason: 'backslash' };
|
|
26
26
|
if (isAbsolute(p)) return { ok: false, reason: 'absolute path' };
|
|
27
27
|
if (!p.startsWith('./')) return { ok: false, reason: 'not ./ relative' };
|
|
28
|
-
|
|
29
|
-
// "./
|
|
30
|
-
const
|
|
31
|
-
for (const seg of
|
|
28
|
+
if (p === './') return { ok: true };
|
|
29
|
+
// The mandatory "./" prefix is not a path component; validate only the declared remainder.
|
|
30
|
+
const segments = p.slice(2).split('/');
|
|
31
|
+
for (const seg of segments) {
|
|
32
32
|
if (seg === '' || seg === '.' || seg === '..') {
|
|
33
33
|
return { ok: false, reason: `dot or parent segment '${seg}'` };
|
|
34
34
|
}
|
|
@@ -214,7 +214,14 @@ function walkTree(
|
|
|
214
214
|
return { fileCount, totalBytes, budgetBlocked };
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
function checkSymlinkContainment(
|
|
217
|
+
function checkSymlinkContainment(root: string, entries: SnapshotEntry[], findings: ValidationFinding[]): void {
|
|
218
|
+
// Canonicalize the owning root once before building the prefix — the raw root may itself
|
|
219
|
+
// contain symlink components (macOS TMPDIR: /var → /private/var), which would make every
|
|
220
|
+
// realpath'd target look like it escapes (same pattern as resolveContained in contained.ts).
|
|
221
|
+
let canonicalRoot = root;
|
|
222
|
+
try {
|
|
223
|
+
canonicalRoot = realpathSync.native(root);
|
|
224
|
+
} catch {}
|
|
218
225
|
for (const e of entries) {
|
|
219
226
|
if (e.type !== 'symlink') continue;
|
|
220
227
|
const abs = join(canonicalRoot, ...e.relPath.split('/'));
|
|
@@ -246,6 +253,28 @@ function checkSymlinkContainment(canonicalRoot: string, entries: SnapshotEntry[]
|
|
|
246
253
|
outcome: `symlink target '${canonical}' escapes owning root`,
|
|
247
254
|
}),
|
|
248
255
|
);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
// CONTEXT: Contained Symlink — canonical target must be a regular file or directory;
|
|
259
|
+
// special-file targets (FIFO/socket/device) are Blocking Findings (same rule as
|
|
260
|
+
// resolveContained in contained.ts). statSync follows the symlink.
|
|
261
|
+
let st: ReturnType<typeof statSync> | undefined;
|
|
262
|
+
try {
|
|
263
|
+
st = statSync(abs);
|
|
264
|
+
} catch {
|
|
265
|
+
continue; // vanished between walk and check; broken/looping already handled above
|
|
266
|
+
}
|
|
267
|
+
if (!st.isFile() && !st.isDirectory()) {
|
|
268
|
+
findings.push(
|
|
269
|
+
blocking({
|
|
270
|
+
code: CODE.CONTAINED_SYMLINK_VIOLATION,
|
|
271
|
+
phase: 'validation',
|
|
272
|
+
target: 'source',
|
|
273
|
+
pointer: e.relPath,
|
|
274
|
+
rule: RULE.CONTAINED_SYMLINK_VIOLATION,
|
|
275
|
+
outcome: `contained symlink target is a special file: '${canonical}' (${st.isFIFO() ? 'fifo' : 'other'})`,
|
|
276
|
+
}),
|
|
277
|
+
);
|
|
249
278
|
}
|
|
250
279
|
}
|
|
251
280
|
}
|