codex-plugin-doctor 1.10.0 → 1.12.1
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 +2 -2
- package/dist/core/review-bundle.js +49 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -358,9 +358,9 @@ jobs:
|
|
|
358
358
|
runs-on: ubuntu-latest
|
|
359
359
|
steps:
|
|
360
360
|
- uses: actions/checkout@v5
|
|
361
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
361
|
+
- uses: Esquetta/CodexPluginDoctor@v1.12.1
|
|
362
362
|
with:
|
|
363
|
-
version: "1.
|
|
363
|
+
version: "1.12.1"
|
|
364
364
|
path: .
|
|
365
365
|
runtime: "true"
|
|
366
366
|
policy: codex-publish
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { buildDoctorAttestation, renderDoctorAttestationJson, verifyDoctorAttestation } from "./attestation.js";
|
|
5
5
|
import { buildDoctorReleaseEvidenceReport, renderDoctorReleaseEvidenceJson, verifyDoctorReleaseEvidence } from "./release-evidence.js";
|
|
@@ -46,6 +46,25 @@ function relativeBundleFiles() {
|
|
|
46
46
|
releaseEvidenceJson: "release-evidence.json"
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
function isPathInsideDirectory(candidatePath, directoryPath) {
|
|
50
|
+
const relativePath = path.relative(directoryPath, candidatePath);
|
|
51
|
+
return relativePath === "" || (!relativePath.startsWith("..") && !path.isAbsolute(relativePath));
|
|
52
|
+
}
|
|
53
|
+
async function resolveBundleArtifactPath(bundleDirectory, relativePath) {
|
|
54
|
+
const resolvedBundleDirectory = path.resolve(bundleDirectory);
|
|
55
|
+
const artifactPath = path.resolve(resolvedBundleDirectory, relativePath);
|
|
56
|
+
if (!isPathInsideDirectory(artifactPath, resolvedBundleDirectory)) {
|
|
57
|
+
throw new Error("Bundle artifact path resolves outside the bundle directory.");
|
|
58
|
+
}
|
|
59
|
+
const [canonicalBundleDirectory, canonicalArtifactPath] = await Promise.all([
|
|
60
|
+
realpath(resolvedBundleDirectory),
|
|
61
|
+
realpath(artifactPath)
|
|
62
|
+
]);
|
|
63
|
+
if (!isPathInsideDirectory(canonicalArtifactPath, canonicalBundleDirectory)) {
|
|
64
|
+
throw new Error("Bundle artifact canonical path resolves outside the bundle directory.");
|
|
65
|
+
}
|
|
66
|
+
return canonicalArtifactPath;
|
|
67
|
+
}
|
|
49
68
|
function bundleStatus(runtimePolicy, releaseEvidence) {
|
|
50
69
|
if (runtimePolicy.status === "fail" || releaseEvidence.status === "fail") {
|
|
51
70
|
return "fail";
|
|
@@ -167,7 +186,7 @@ export function renderDoctorReviewBundleJson(bundle) {
|
|
|
167
186
|
return JSON.stringify(bundle.manifest, null, 2);
|
|
168
187
|
}
|
|
169
188
|
async function readBundleJsonFile(bundleDirectory, relativePath) {
|
|
170
|
-
return readJsonFile(
|
|
189
|
+
return readJsonFile(await resolveBundleArtifactPath(bundleDirectory, relativePath));
|
|
171
190
|
}
|
|
172
191
|
async function readBundleDiffSnapshot(bundleDirectory) {
|
|
173
192
|
const manifestArtifact = await readBundleJsonFile(bundleDirectory, "manifest.json");
|
|
@@ -344,7 +363,8 @@ export async function verifyDoctorReviewBundle(bundleDirectory, options) {
|
|
|
344
363
|
const files = manifest?.files ?? relativeBundleFiles();
|
|
345
364
|
for (const [fileKey, relativePath] of Object.entries(files)) {
|
|
346
365
|
try {
|
|
347
|
-
const
|
|
366
|
+
const artifactPath = await resolveBundleArtifactPath(resolvedBundleDirectory, relativePath);
|
|
367
|
+
const fileStat = await stat(artifactPath);
|
|
348
368
|
checks.push({
|
|
349
369
|
id: `review_bundle.file.${fileKey}`,
|
|
350
370
|
status: fileStat.isFile() ? "pass" : "fail",
|
|
@@ -404,7 +424,28 @@ export async function verifyDoctorReviewBundle(bundleDirectory, options) {
|
|
|
404
424
|
continue;
|
|
405
425
|
}
|
|
406
426
|
try {
|
|
407
|
-
const
|
|
427
|
+
const declaredPath = files[fileKey];
|
|
428
|
+
if (expected.path !== declaredPath) {
|
|
429
|
+
integrityStatus = "fail";
|
|
430
|
+
checks.push({
|
|
431
|
+
id: `review_bundle.integrity.${fileKey}.path`,
|
|
432
|
+
status: "fail",
|
|
433
|
+
message: `${expected.path} does not match the declared bundle file path.`
|
|
434
|
+
});
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const resolvedIntegrityPath = path.resolve(resolvedBundleDirectory, expected.path);
|
|
438
|
+
if (!isPathInsideDirectory(resolvedIntegrityPath, resolvedBundleDirectory)) {
|
|
439
|
+
integrityStatus = "fail";
|
|
440
|
+
checks.push({
|
|
441
|
+
id: `review_bundle.integrity.${fileKey}.path`,
|
|
442
|
+
status: "fail",
|
|
443
|
+
message: `${expected.path} resolves outside the review bundle directory.`
|
|
444
|
+
});
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const integrityPath = await resolveBundleArtifactPath(resolvedBundleDirectory, expected.path);
|
|
448
|
+
const content = await readFile(integrityPath);
|
|
408
449
|
const digest = sha256(content);
|
|
409
450
|
const matches = digest === expected.digest && content.byteLength === expected.bytes;
|
|
410
451
|
if (!matches) {
|
|
@@ -472,7 +513,8 @@ export async function verifyDoctorReviewBundle(bundleDirectory, options) {
|
|
|
472
513
|
});
|
|
473
514
|
}
|
|
474
515
|
try {
|
|
475
|
-
|
|
516
|
+
const attestationPath = await resolveBundleArtifactPath(resolvedBundleDirectory, files.attestationJson);
|
|
517
|
+
attestation = await verifyDoctorAttestation(attestationPath, targetPath, { signingKey: options.signingKey });
|
|
476
518
|
checks.push({
|
|
477
519
|
id: "review_bundle.attestation",
|
|
478
520
|
status: attestation.status,
|
|
@@ -489,7 +531,8 @@ export async function verifyDoctorReviewBundle(bundleDirectory, options) {
|
|
|
489
531
|
});
|
|
490
532
|
}
|
|
491
533
|
try {
|
|
492
|
-
|
|
534
|
+
const releaseEvidencePath = await resolveBundleArtifactPath(resolvedBundleDirectory, files.releaseEvidenceJson);
|
|
535
|
+
releaseEvidence = await verifyDoctorReleaseEvidence(releaseEvidencePath, {
|
|
493
536
|
signingKey: options.signingKey,
|
|
494
537
|
targetPath
|
|
495
538
|
});
|
package/package.json
CHANGED