mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-02 07:22:32 +00:00
246 lines
10 KiB
JavaScript
246 lines
10 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "fs";
|
|
import { test } from "node:test";
|
|
import {
|
|
EXTERNAL_PLUGINS_FILE,
|
|
validateExternalPlugin,
|
|
validateExternalPlugins,
|
|
} from "./external-plugin-validation.mjs";
|
|
import { validateLicenseField } from "./lib/license.mjs";
|
|
|
|
function basePlugin(overrides = {}) {
|
|
return {
|
|
name: "example-plugin",
|
|
description: "An example external plugin used for validation tests.",
|
|
version: "1.2.3",
|
|
author: { name: "Example Author", url: "https://github.com/example" },
|
|
repository: "https://github.com/example/example-plugin",
|
|
license: "MIT",
|
|
keywords: ["example", "testing"],
|
|
source: {
|
|
source: "github",
|
|
repo: "example/example-plugin",
|
|
ref: "v1.2.3",
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function hasError(result, needle) {
|
|
return result.errors.some((message) => message.includes(needle));
|
|
}
|
|
|
|
function hasWarning(result, needle) {
|
|
return result.warnings.some((message) => message.includes(needle));
|
|
}
|
|
|
|
function longestBacktickRun(value) {
|
|
return Math.max(0, ...Array.from(value.matchAll(/`+/g), (match) => match[0].length));
|
|
}
|
|
|
|
test("valid plugin has no errors under both policies", () => {
|
|
const marketplace = validateExternalPlugin(basePlugin(), 0, { policy: "marketplace" });
|
|
assert.deepEqual(marketplace.errors, []);
|
|
|
|
const publicSubmission = validateExternalPlugin(
|
|
basePlugin({ source: { source: "github", repo: "example/example-plugin", ref: "v1.2.3" } }),
|
|
0,
|
|
{ policy: "publicSubmission" }
|
|
);
|
|
assert.deepEqual(publicSubmission.errors, []);
|
|
});
|
|
|
|
test("version must be semver", () => {
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ version: "1.2.3" }), 0).errors, []);
|
|
// Prerelease metadata is allowed (matches committed 1.0.1161-preview1).
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ version: "1.0.1161-preview1" }), 0).errors, []);
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ version: "1.2.3-beta.1+build.5" }), 0).errors, []);
|
|
|
|
assert.ok(hasError(validateExternalPlugin(basePlugin({ version: "1.0" }), 0), "semantic version"));
|
|
assert.ok(hasError(validateExternalPlugin(basePlugin({ version: "v1.2.3" }), 0), "semantic version"));
|
|
assert.ok(hasError(validateExternalPlugin(basePlugin({ version: "latest" }), 0), "semantic version"));
|
|
});
|
|
|
|
test("license accepts SPDX ids and expressions", () => {
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ license: "Apache-2.0" }), 0).errors, []);
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ license: "MIT OR Apache-2.0" }), 0).errors, []);
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ license: "(MIT OR Apache-2.0)" }), 0).errors, []);
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ license: "LicenseRef-Custom" }), 0).errors, []);
|
|
|
|
const recognized = validateExternalPlugin(basePlugin({ license: "MIT" }), 0);
|
|
assert.equal(recognized.warnings.filter((m) => m.includes("license")).length, 0);
|
|
});
|
|
|
|
test("non-SPDX license is a warning, never an error", () => {
|
|
for (const license of ["SSAL-1.0", "Proprietary", "UNLICENSED", "SEE LICENSE IN LICENSE.md", "MIT OR"]) {
|
|
const result = validateExternalPlugin(basePlugin({ license }), 0);
|
|
assert.deepEqual(result.errors, [], `expected no errors for license "${license}"`);
|
|
assert.ok(
|
|
hasWarning(result, "not a recognized SPDX identifier"),
|
|
`expected a warning for license "${license}"`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("empty/non-string license is an error; required license is enforced", () => {
|
|
assert.ok(hasError(validateExternalPlugin(basePlugin({ license: "" }), 0), "non-empty string"));
|
|
assert.ok(hasError(validateExternalPlugin(basePlugin({ license: 42 }), 0), "non-empty string"));
|
|
|
|
// publicSubmission requires a license.
|
|
const noLicense = basePlugin();
|
|
delete noLicense.license;
|
|
assert.ok(
|
|
hasError(validateExternalPlugin(noLicense, 0, { policy: "publicSubmission" }), '"license" is required')
|
|
);
|
|
});
|
|
|
|
test("validateLicenseField is reusable for local plugin.json", () => {
|
|
assert.deepEqual(validateLicenseField("MIT"), { errors: [], warnings: [] });
|
|
|
|
const proprietary = validateLicenseField("Proprietary");
|
|
assert.deepEqual(proprietary.errors, []);
|
|
assert.equal(proprietary.warnings.length, 1);
|
|
|
|
// Optional by default: absent license produces nothing.
|
|
assert.deepEqual(validateLicenseField(undefined), { errors: [], warnings: [] });
|
|
assert.ok(validateLicenseField(undefined, { required: true }).errors.length === 1);
|
|
});
|
|
|
|
test("license grammar validates refs, parentheses, WITH exceptions, and warning sanitization", () => {
|
|
for (const license of [
|
|
"GPL-2.0-only WITH Classpath-exception-2.0",
|
|
"LicenseRef-Custom",
|
|
"DocumentRef-spdx-tool:LicenseRef-MyLicense",
|
|
"(MIT OR Apache-2.0)",
|
|
"MIT AND Apache-2.0",
|
|
]) {
|
|
const result = validateLicenseField(license);
|
|
assert.deepEqual(result.errors, [], `expected no errors for recognized license "${license}"`);
|
|
assert.equal(result.warnings.length, 0, `expected no warnings for recognized license "${license}"`);
|
|
}
|
|
|
|
for (const license of [
|
|
"LicenseRef-",
|
|
"LicenseRef-@@",
|
|
"DocumentRef-foo",
|
|
"(MIT",
|
|
"MIT)",
|
|
"MIT OR (Apache-2.0))",
|
|
"(MIT OR Apache-2.0",
|
|
"MIT WITH Apache-2.0",
|
|
"GPL-2.0-only WITH MIT",
|
|
]) {
|
|
const result = validateLicenseField(license);
|
|
assert.deepEqual(result.errors, [], `expected malformed license "${license}" to warn, not error`);
|
|
assert.equal(result.warnings.length, 1, `expected one warning for malformed license "${license}"`);
|
|
assert.ok(hasWarning(result, "not a recognized SPDX identifier"));
|
|
}
|
|
|
|
const injected = validateLicenseField("MIT\n\n## Injected\n| a | b |");
|
|
assert.deepEqual(injected.errors, []);
|
|
assert.equal(injected.warnings.length, 1);
|
|
assert.ok(!injected.warnings[0].includes("\n"));
|
|
assert.ok(hasWarning(injected, "not a recognized SPDX identifier"));
|
|
|
|
const backslashed = validateLicenseField("MIT \\ Custom \\`code\\`");
|
|
assert.deepEqual(backslashed.errors, []);
|
|
assert.equal(backslashed.warnings.length, 1);
|
|
assert.ok(backslashed.warnings[0].includes("`` MIT \\ Custom \\`code\\` ``"));
|
|
});
|
|
|
|
test("license warning wraps backticks and markdown link in an unbreakable code span", () => {
|
|
const license = "x` [pwn](https://evil.example)";
|
|
const result = validateLicenseField(license);
|
|
|
|
assert.deepEqual(result.errors, []);
|
|
assert.equal(result.warnings.length, 1);
|
|
|
|
const warning = result.warnings[0];
|
|
assert.ok(warning.includes("is not a recognized SPDX identifier"));
|
|
const [, wrappedValue] = warning.match(/"license" value (.+?) is not a recognized SPDX identifier/u);
|
|
const fence = wrappedValue.match(/^`+/u)[0];
|
|
const content = wrappedValue.slice(fence.length, -fence.length);
|
|
|
|
assert.ok(fence.length > longestBacktickRun(content));
|
|
assert.ok(content.includes("[pwn](https://evil.example)"));
|
|
});
|
|
|
|
test("author.email is validated only when present", () => {
|
|
assert.deepEqual(
|
|
validateExternalPlugin(basePlugin({ author: { name: "Example", email: "dev@example.com" } }), 0).errors,
|
|
[]
|
|
);
|
|
assert.ok(
|
|
hasError(
|
|
validateExternalPlugin(basePlugin({ author: { name: "Example", email: "not-an-email" } }), 0),
|
|
"author.email"
|
|
)
|
|
);
|
|
// Absent email must not raise an error.
|
|
assert.deepEqual(validateExternalPlugin(basePlugin({ author: { name: "Example" } }), 0).errors, []);
|
|
});
|
|
|
|
test("unknown fields produce warnings, not errors", () => {
|
|
const topLevel = validateExternalPlugin(basePlugin({ licence: "MIT" }), 0);
|
|
assert.deepEqual(topLevel.errors, []);
|
|
assert.ok(hasWarning(topLevel, "unknown top-level field `licence`"));
|
|
|
|
const authorTypo = validateExternalPlugin(
|
|
basePlugin({ author: { name: "Example", emial: "dev@example.com" } }),
|
|
0
|
|
);
|
|
assert.ok(hasWarning(authorTypo, "unknown author field `author.emial`"));
|
|
|
|
const sourceTypo = validateExternalPlugin(
|
|
basePlugin({ source: { source: "github", repo: "example/example-plugin", ref: "v1.2.3", branch: "main" } }),
|
|
0
|
|
);
|
|
assert.ok(hasWarning(sourceTypo, "unknown source field `source.branch`"));
|
|
|
|
// A typo in the source discriminator itself is still surfaced as an unknown
|
|
// source field (plus the discriminator error), because the unknown-field check
|
|
// now runs for every source object rather than only recognized github sources.
|
|
const sourceDiscriminatorTypo = validateExternalPlugin(
|
|
basePlugin({ source: { soruce: "github", repo: "example/example-plugin", ref: "v1.2.3" } }),
|
|
0
|
|
);
|
|
assert.ok(hasWarning(sourceDiscriminatorTypo, "unknown source field `source.soruce`"));
|
|
assert.ok(hasError(sourceDiscriminatorTypo, '"source.source" must be one of'));
|
|
|
|
// Supported fields never warn.
|
|
const clean = validateExternalPlugin(basePlugin({ homepage: "https://github.com/example/example-plugin" }), 0);
|
|
assert.equal(
|
|
clean.warnings.filter((message) => message.includes("unknown")).length,
|
|
0
|
|
);
|
|
});
|
|
|
|
test("unknown field warning collapses malicious key markdown into one inline code span", () => {
|
|
const maliciousKey = "evil\n- injected [x](https://evil.example)";
|
|
const result = validateExternalPlugin(basePlugin({ [maliciousKey]: true }), 0);
|
|
|
|
assert.deepEqual(result.errors, []);
|
|
const warning = result.warnings.find((message) => message.includes("unknown top-level field"));
|
|
assert.ok(warning);
|
|
assert.ok(!warning.includes("\n"));
|
|
assert.ok(warning.includes("unknown top-level field `evil - injected [x](https://evil.example)`"));
|
|
assert.ok(warning.includes("(possible typo)"));
|
|
});
|
|
|
|
test("immutable locator: marketplace warns, publicSubmission errors", () => {
|
|
const noLocator = basePlugin({ source: { source: "github", repo: "example/example-plugin" } });
|
|
|
|
const marketplace = validateExternalPlugin(noLocator, 0, { policy: "marketplace" });
|
|
assert.deepEqual(marketplace.errors, []);
|
|
assert.ok(hasWarning(marketplace, "immutable tag ref or commit SHA is recommended"));
|
|
|
|
const publicSubmission = validateExternalPlugin(noLocator, 0, { policy: "publicSubmission" });
|
|
assert.ok(hasError(publicSubmission, 'one of "source.ref" or "source.sha" is required'));
|
|
});
|
|
|
|
test("committed external.json passes marketplace policy with zero errors", () => {
|
|
const plugins = JSON.parse(fs.readFileSync(EXTERNAL_PLUGINS_FILE, "utf8"));
|
|
const { errors } = validateExternalPlugins(plugins, { policy: "marketplace" });
|
|
assert.deepEqual(errors, [], `Committed external.json produced errors:\n${errors.join("\n")}`);
|
|
});
|