codecartographer-pi 0.12.9 → 0.12.10
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/dist/core/utils.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ export declare function pathExists(path: string): Promise<boolean>;
|
|
|
3
3
|
export declare function canonicalPath(path: string): Promise<string>;
|
|
4
4
|
export declare function normalizeForComparison(path: string): string;
|
|
5
5
|
export declare function isWithinPath(path: string, root: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Symlink-aware version of isWithinPath. Resolves symlinks on both the path
|
|
8
|
+
* and root before comparing, preventing bypass via symlinks inside the
|
|
9
|
+
* allowed root that point outside it.
|
|
10
|
+
*
|
|
11
|
+
* Falls back to lexical isWithinPath if realpath fails (e.g., path does not
|
|
12
|
+
* exist yet), which is safe for write targets that haven't been created.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isWithinPathResolved(path: string, root: string): Promise<boolean>;
|
|
6
15
|
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
7
16
|
export declare function uniqueStrings(items: string[]): string[];
|
|
8
17
|
export declare function dateOnly(timestamp: string): string;
|
package/dist/core/utils.js
CHANGED
|
@@ -36,6 +36,27 @@ export function isWithinPath(path, root) {
|
|
|
36
36
|
return true;
|
|
37
37
|
return normalizedPath.startsWith(`${normalizedRoot}${process.platform === "win32" ? "\\" : "/"}`);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Symlink-aware version of isWithinPath. Resolves symlinks on both the path
|
|
41
|
+
* and root before comparing, preventing bypass via symlinks inside the
|
|
42
|
+
* allowed root that point outside it.
|
|
43
|
+
*
|
|
44
|
+
* Falls back to lexical isWithinPath if realpath fails (e.g., path does not
|
|
45
|
+
* exist yet), which is safe for write targets that haven't been created.
|
|
46
|
+
*/
|
|
47
|
+
export async function isWithinPathResolved(path, root) {
|
|
48
|
+
try {
|
|
49
|
+
const resolvedPath = await realpath(path);
|
|
50
|
+
const resolvedRoot = await realpath(root);
|
|
51
|
+
return isWithinPath(resolvedPath, resolvedRoot);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// If realpath fails (path doesn't exist yet, broken symlink, etc.),
|
|
55
|
+
// fall back to lexical check. For write targets this is safe because
|
|
56
|
+
// the parent directory should already be within the root.
|
|
57
|
+
return isWithinPath(path, root);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
39
60
|
export function isPlainObject(value) {
|
|
40
61
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
62
|
}
|
|
@@ -8,7 +8,7 @@ import { narrateDashboard } from "./dashboard-narrator.js";
|
|
|
8
8
|
import { writeDashboard } from "./dashboard-writer.js";
|
|
9
9
|
import { parseNextFlags } from "./next-flags.js";
|
|
10
10
|
import { phaseCompactionExtension } from "./phase-compaction.js";
|
|
11
|
-
import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState,
|
|
11
|
+
import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPathResolved, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, packagedWorkspaceDir, pathExists, PACKAGE_VERSION, PhasePreflightError, PIPELINE_ALIASES, publishEntry, resolvePhase, resolvePipelineChoice, runPhasePreflight, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../../core/index.js";
|
|
12
12
|
import { initLibrary } from "../../core/library.js";
|
|
13
13
|
import { resolveUserConfigPath } from "../../core/orchestrator-config.js";
|
|
14
14
|
const STATUS_WIDGET_ID = "codecarto-widget";
|
|
@@ -167,7 +167,8 @@ export default function codeCartographerExtension(pi) {
|
|
|
167
167
|
if (config.library.path && await discoverLibrary(config.library.path)) {
|
|
168
168
|
allowedRoots.push(await canonicalPath(config.library.path));
|
|
169
169
|
}
|
|
170
|
-
|
|
170
|
+
const withinAllowed = await Promise.all(allowedRoots.map((allowedRoot) => isWithinPathResolved(targetPath, allowedRoot)));
|
|
171
|
+
if (!withinAllowed.some((result) => result)) {
|
|
171
172
|
if (ctx.hasUI) {
|
|
172
173
|
ctx.ui.notify(`Blocked ${event.toolName} outside .codecarto/ or configured library: ${inputPath}`, "warning");
|
|
173
174
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.10",
|
|
4
4
|
"mcpName": "io.github.HuginnIndustries/codecartographer",
|
|
5
5
|
"description": "Evidence-backed reverse engineering and human-gated software planning for Pi and MCP coding agents.",
|
|
6
6
|
"type": "module",
|