codex-plugin-doctor 1.34.0 → 1.35.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/README.md
CHANGED
|
@@ -402,9 +402,9 @@ jobs:
|
|
|
402
402
|
runs-on: ubuntu-latest
|
|
403
403
|
steps:
|
|
404
404
|
- uses: actions/checkout@v5
|
|
405
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
406
|
-
with:
|
|
407
|
-
version: "1.
|
|
405
|
+
- uses: Esquetta/CodexPluginDoctor@v1.35.0
|
|
406
|
+
with:
|
|
407
|
+
version: "1.35.0"
|
|
408
408
|
path: .
|
|
409
409
|
runtime: "true"
|
|
410
410
|
policy: codex-publish
|
|
@@ -30,6 +30,11 @@ function statusFromCheckResult(result) {
|
|
|
30
30
|
}
|
|
31
31
|
return "pass";
|
|
32
32
|
}
|
|
33
|
+
function isPathWithinRoot(rootPath, candidatePath) {
|
|
34
|
+
const relativePath = path.relative(rootPath, candidatePath);
|
|
35
|
+
return (relativePath === "" ||
|
|
36
|
+
(!relativePath.startsWith("..") && !path.isAbsolute(relativePath)));
|
|
37
|
+
}
|
|
33
38
|
export async function readMcpConfigPath(targetPath) {
|
|
34
39
|
const rootPath = path.resolve(targetPath);
|
|
35
40
|
const directMcpPath = path.join(rootPath, ".mcp.json");
|
|
@@ -40,21 +45,38 @@ export async function readMcpConfigPath(targetPath) {
|
|
|
40
45
|
if (!(await fileExists(manifestPath))) {
|
|
41
46
|
return null;
|
|
42
47
|
}
|
|
48
|
+
let manifest;
|
|
43
49
|
try {
|
|
44
|
-
|
|
45
|
-
return typeof manifest.mcpServers === "string"
|
|
46
|
-
? path.resolve(rootPath, manifest.mcpServers)
|
|
47
|
-
: null;
|
|
50
|
+
manifest = await readJsonFile(manifestPath);
|
|
48
51
|
}
|
|
49
52
|
catch {
|
|
50
53
|
return null;
|
|
51
54
|
}
|
|
55
|
+
if (typeof manifest.mcpServers !== "string") {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const mcpConfigPath = path.resolve(rootPath, manifest.mcpServers);
|
|
59
|
+
if (!isPathWithinRoot(rootPath, mcpConfigPath)) {
|
|
60
|
+
throw new Error("Manifest MCP config path resolves outside the package root.");
|
|
61
|
+
}
|
|
62
|
+
return mcpConfigPath;
|
|
52
63
|
}
|
|
53
64
|
async function hasCodexManifest(targetPath) {
|
|
54
65
|
return fileExists(path.join(path.resolve(targetPath), ".codex-plugin", "plugin.json"));
|
|
55
66
|
}
|
|
56
67
|
async function checkGenericMcp(targetPath) {
|
|
57
|
-
|
|
68
|
+
let mcpConfigPath;
|
|
69
|
+
try {
|
|
70
|
+
mcpConfigPath = await readMcpConfigPath(targetPath);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return {
|
|
74
|
+
client: "Generic MCP",
|
|
75
|
+
status: "fail",
|
|
76
|
+
summary: error instanceof Error ? error.message : "MCP config path is unsafe.",
|
|
77
|
+
details: ["Keep `.mcp.json` or manifest `mcpServers` references inside the package root."]
|
|
78
|
+
};
|
|
79
|
+
}
|
|
58
80
|
if (!mcpConfigPath || !(await fileExists(mcpConfigPath))) {
|
|
59
81
|
return {
|
|
60
82
|
client: "Generic MCP",
|
|
@@ -193,12 +193,20 @@ async function fileExists(targetPath) {
|
|
|
193
193
|
return false;
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
+
function isPathWithinRoot(rootPath, candidatePath) {
|
|
197
|
+
const relativePath = path.relative(rootPath, candidatePath);
|
|
198
|
+
return (relativePath === "" ||
|
|
199
|
+
(!relativePath.startsWith("..") && !path.isAbsolute(relativePath)));
|
|
200
|
+
}
|
|
196
201
|
async function loadMcpServers(discoveredPackage) {
|
|
197
202
|
const { manifest, rootPath } = discoveredPackage;
|
|
198
203
|
if (!manifest.mcpServers) {
|
|
199
204
|
return null;
|
|
200
205
|
}
|
|
201
206
|
const mcpConfigPath = path.resolve(rootPath, manifest.mcpServers);
|
|
207
|
+
if (!isPathWithinRoot(rootPath, mcpConfigPath)) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
202
210
|
const exists = await fileExists(mcpConfigPath);
|
|
203
211
|
if (!exists) {
|
|
204
212
|
return null;
|
package/package.json
CHANGED