@droposs/plugin-cli 0.6.0 → 0.6.3
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/builder.js +2 -2
- package/dist/signer.d.ts +8 -1
- package/dist/signer.js +83 -4
- package/package.json +2 -2
package/dist/builder.js
CHANGED
|
@@ -35,7 +35,7 @@ export async function buildPlugin(targetDir = ".", options = {}) {
|
|
|
35
35
|
sourcemap: options.sourcemap ?? true,
|
|
36
36
|
minify: options.minify ?? false,
|
|
37
37
|
external: [
|
|
38
|
-
"@
|
|
38
|
+
"@droposs/plugin-sdk",
|
|
39
39
|
"@droposs/plugin-sdk",
|
|
40
40
|
"@drop/plugin-sdk",
|
|
41
41
|
"h3",
|
|
@@ -71,7 +71,7 @@ export async function buildPlugin(targetDir = ".", options = {}) {
|
|
|
71
71
|
minify: options.minify ?? false,
|
|
72
72
|
external: [
|
|
73
73
|
"vue",
|
|
74
|
-
"@
|
|
74
|
+
"@droposs/plugin-sdk",
|
|
75
75
|
"@droposs/plugin-sdk",
|
|
76
76
|
"@drop/plugin-sdk",
|
|
77
77
|
],
|
package/dist/signer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SIGNATURE_VERSION } from "@drop-oss/plugin-sdk";
|
|
1
|
+
import { SIGNATURE_VERSION, type PluginManifest } from "@drop-oss/plugin-sdk";
|
|
2
2
|
/** Current signature scheme; re-exported for backwards compatibility. */
|
|
3
3
|
export { SIGNATURE_VERSION };
|
|
4
4
|
/**
|
|
@@ -39,6 +39,13 @@ export interface VerifyResult {
|
|
|
39
39
|
export declare function verifyPlugin(targetDir: string, signingKey?: string, options?: {
|
|
40
40
|
allowUnsigned?: boolean;
|
|
41
41
|
}): Promise<VerifyResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Validate the `client.sidecars` declaration against the bundle contents:
|
|
44
|
+
* each target path must resolve inside the bundle, point to a present regular
|
|
45
|
+
* file covered by the `files` checksums, and cite a matching SHA-256 of the
|
|
46
|
+
* binary. Every sidecar name must be allowlisted in `client.commands`, and
|
|
47
|
+
*/
|
|
48
|
+
export declare function verifySidecars(bundleDir: string, manifest: PluginManifest & Record<string, unknown>, files: string[], present: Set<string>, errors: string[]): Promise<void>;
|
|
42
49
|
export declare function packPlugin(targetDir: string, outputDir?: string): Promise<{
|
|
43
50
|
packagePath: string;
|
|
44
51
|
id: string;
|
package/dist/signer.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readdir, readFile, realpath, stat, writeFile, mkdir, } from "node:fs/pr
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import Ajv from "ajv";
|
|
6
|
-
import { SIGNATURE_VERSION } from "@
|
|
6
|
+
import { SIGNATURE_VERSION } from "@droposs/plugin-sdk";
|
|
7
7
|
const MANIFEST_FILE = "drop-plugin.json";
|
|
8
8
|
/** Current signature scheme; re-exported for backwards compatibility. */
|
|
9
9
|
export { SIGNATURE_VERSION };
|
|
@@ -73,7 +73,7 @@ export function isInside(base, candidate) {
|
|
|
73
73
|
async function loadSchema() {
|
|
74
74
|
const require = createRequire(import.meta.url);
|
|
75
75
|
for (const specifier of [
|
|
76
|
-
"@
|
|
76
|
+
"@droposs/plugin-sdk/schema.json",
|
|
77
77
|
"@droposs/plugin-sdk/schema.json",
|
|
78
78
|
]) {
|
|
79
79
|
try {
|
|
@@ -91,7 +91,6 @@ let cachedErrors = [];
|
|
|
91
91
|
export async function validateManifest(manifest) {
|
|
92
92
|
if (!cachedValidator) {
|
|
93
93
|
const schema = await loadSchema();
|
|
94
|
-
// @ts-ignore
|
|
95
94
|
const AjvClass = Ajv.default ?? Ajv;
|
|
96
95
|
const ajv = new AjvClass({ allErrors: true, strict: false });
|
|
97
96
|
const compiled = ajv.compile(schema);
|
|
@@ -189,6 +188,13 @@ export async function signPlugin(targetDir, signingKey, validate = true, options
|
|
|
189
188
|
if (!validation.valid) {
|
|
190
189
|
throw new Error(`Manifest validation failed against schema:\n ${validation.errors.join("\n ")}`);
|
|
191
190
|
}
|
|
191
|
+
const files = await listFiles(bundleDir);
|
|
192
|
+
const present = new Set(files);
|
|
193
|
+
const sidecarErrors = [];
|
|
194
|
+
await verifySidecars(bundleDir, manifest, files, present, sidecarErrors);
|
|
195
|
+
if (sidecarErrors.length > 0) {
|
|
196
|
+
throw new Error(`Sidecar validation failed:\n ${sidecarErrors.join("\n ")}`);
|
|
197
|
+
}
|
|
192
198
|
}
|
|
193
199
|
const derived = await deriveManifest(bundleDir, manifest, signingKey);
|
|
194
200
|
const outPath = options.outManifest
|
|
@@ -246,6 +252,7 @@ export async function verifyPlugin(targetDir, signingKey, options = {}) {
|
|
|
246
252
|
errors.push("bundle contains multiple code files but no 'files' checksums; refusing unverified imports");
|
|
247
253
|
}
|
|
248
254
|
const entry = manifest.entry ?? manifest.server?.entry ?? manifest.client?.entry;
|
|
255
|
+
await verifySidecars(bundleDir, manifest, files, present, errors);
|
|
249
256
|
if (entry) {
|
|
250
257
|
const entryPath = path.resolve(bundleDir, entry);
|
|
251
258
|
if (!isInside(bundleDir, entryPath)) {
|
|
@@ -320,6 +327,72 @@ async function resolveSignedPayload(bundleDir, manifest, filesAggregate, entry)
|
|
|
320
327
|
error: "legacy signature has neither file checksums nor an entry checksum",
|
|
321
328
|
};
|
|
322
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Validate the `client.sidecars` declaration against the bundle contents:
|
|
332
|
+
* each target path must resolve inside the bundle, point to a present regular
|
|
333
|
+
* file covered by the `files` checksums, and cite a matching SHA-256 of the
|
|
334
|
+
* binary. Every sidecar name must be allowlisted in `client.commands`, and
|
|
335
|
+
*/
|
|
336
|
+
export async function verifySidecars(bundleDir, manifest, files, present, errors) {
|
|
337
|
+
const sidecars = manifest.client?.sidecars;
|
|
338
|
+
if (sidecars === undefined)
|
|
339
|
+
return;
|
|
340
|
+
if (!Array.isArray(sidecars) || sidecars.length === 0) {
|
|
341
|
+
errors.push("client.sidecars must be a non-empty array when declared");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const commands = new Set(Array.isArray(manifest.client?.commands) ? manifest.client.commands : []);
|
|
345
|
+
for (const [idx, sidecar] of sidecars.entries()) {
|
|
346
|
+
const label = `client.sidecars[${idx}]`;
|
|
347
|
+
if (typeof sidecar !== "object" ||
|
|
348
|
+
sidecar === null ||
|
|
349
|
+
typeof sidecar.name !== "string" ||
|
|
350
|
+
!Array.isArray(sidecar.targets)) {
|
|
351
|
+
errors.push(`${label}: expected { name: string, targets: array }`);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const { name, targets } = sidecar;
|
|
355
|
+
if (!commands.has(name)) {
|
|
356
|
+
errors.push(`${label}: sidecar name '${name}' must be allowlisted in client.commands`);
|
|
357
|
+
}
|
|
358
|
+
const seenTargets = new Set();
|
|
359
|
+
for (const [tIdx, target] of targets.entries()) {
|
|
360
|
+
const tLabel = `${label}.targets[${tIdx}]`;
|
|
361
|
+
if (!target || typeof target !== "object") {
|
|
362
|
+
errors.push(`${tLabel}: expected target object`);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (typeof target.os !== "string" || typeof target.arch !== "string") {
|
|
366
|
+
errors.push(`${tLabel}: expected string os and arch`);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const key = `${target.os}-${target.arch}`;
|
|
370
|
+
if (seenTargets.has(key)) {
|
|
371
|
+
errors.push(`${tLabel}: duplicate target '${key}' (only one binary per os+arch for sidecar '${name}')`);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
seenTargets.add(key);
|
|
375
|
+
if (typeof target.path !== "string" ||
|
|
376
|
+
path.isAbsolute(target.path) ||
|
|
377
|
+
!isInside(bundleDir, path.resolve(bundleDir, target.path))) {
|
|
378
|
+
errors.push(`${tLabel}: path must be a bundle-relative path`);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (isBundleCodeFile(target.path)) {
|
|
382
|
+
errors.push(`${tLabel}: 'sidecars' paths must not be JavaScript code files`);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (!present.has(target.path)) {
|
|
386
|
+
errors.push(`${tLabel}: declared sidecar file missing: ${target.path}`);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const digest = sha256Hex(await readFile(path.join(bundleDir, target.path)));
|
|
390
|
+
if (target.sha256 !== digest) {
|
|
391
|
+
errors.push(`${tLabel}: sha256 mismatch for ${target.path} (declared ${target.sha256}, actual ${digest})`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
323
396
|
export async function packPlugin(targetDir, outputDir) {
|
|
324
397
|
const resolvedPath = path.resolve(process.cwd(), targetDir);
|
|
325
398
|
const bundleDir = await realpath(resolvedPath).catch(() => null);
|
|
@@ -332,6 +405,13 @@ export async function packPlugin(targetDir, outputDir) {
|
|
|
332
405
|
if (!validation.valid) {
|
|
333
406
|
throw new Error(`Manifest validation failed against schema:\n ${validation.errors.join("\n ")}`);
|
|
334
407
|
}
|
|
408
|
+
const files = await listFiles(bundleDir);
|
|
409
|
+
const present = new Set(files);
|
|
410
|
+
const sidecarErrors = [];
|
|
411
|
+
await verifySidecars(bundleDir, rawManifest, files, present, sidecarErrors);
|
|
412
|
+
if (sidecarErrors.length > 0) {
|
|
413
|
+
throw new Error(`Sidecar validation failed:\n ${sidecarErrors.join("\n ")}`);
|
|
414
|
+
}
|
|
335
415
|
// Derive the signed manifest in memory: packing must not dirty the source
|
|
336
416
|
// tree, the derived fields live in the archive only.
|
|
337
417
|
const { manifest } = await deriveManifest(bundleDir, rawManifest);
|
|
@@ -344,7 +424,6 @@ export async function packPlugin(targetDir, outputDir) {
|
|
|
344
424
|
? path.resolve(process.cwd(), outputDir)
|
|
345
425
|
: path.join(bundleDir, "dist-package");
|
|
346
426
|
await mkdir(outDir, { recursive: true });
|
|
347
|
-
const files = await listFiles(bundleDir);
|
|
348
427
|
const bundleMap = {};
|
|
349
428
|
for (const rel of files) {
|
|
350
429
|
const content = await readFile(path.join(bundleDir, rel));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@droposs/plugin-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Drop Plugin build, test, signing, and packaging CLI for Drop OSS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,6 +50,6 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"ajv": "^8.20.0",
|
|
52
52
|
"esbuild": "^0.28.2",
|
|
53
|
-
"@droposs/plugin-sdk": "0.6.
|
|
53
|
+
"@droposs/plugin-sdk": "0.6.3"
|
|
54
54
|
}
|
|
55
55
|
}
|