📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -135,7 +135,7 @@ assert.match(
|
||||
);
|
||||
assert.match(
|
||||
ciWorkflow,
|
||||
/source-validation:[\s\S]*?- uses: actions\/checkout@v\d+[\s\S]*?with:[\s\S]*?fetch-depth: 0/,
|
||||
/source-validation:[\s\S]*?- uses: actions\/checkout@[a-f0-9]{40}[\s\S]*?with:[\s\S]*?fetch-depth: 0/,
|
||||
"source-validation should use an unshallowed checkout so base-branch diffs have a merge base",
|
||||
);
|
||||
assert.match(
|
||||
|
||||
@@ -14,18 +14,31 @@ const bundles = bundleData.bundles || {};
|
||||
const catalog = JSON.parse(
|
||||
fs.readFileSync(path.join(repoRoot, "data", "catalog.json"), "utf8"),
|
||||
);
|
||||
const canonicalIndex = JSON.parse(
|
||||
fs.readFileSync(path.join(repoRoot, "skills_index.json"), "utf8"),
|
||||
);
|
||||
const skillsById = new Map(catalog.skills.map((skill) => [skill.id, skill]));
|
||||
|
||||
assert.strictEqual(
|
||||
skillsById.get("before-you-build").category,
|
||||
"business",
|
||||
"explicit product frontmatter should keep product-risk skills out of security",
|
||||
"product",
|
||||
"catalog categories must match the canonical skills index",
|
||||
);
|
||||
assert.ok(
|
||||
!bundles["security-core"].skills.includes("before-you-build"),
|
||||
"explicit product frontmatter should keep product-risk skills out of the security bundle",
|
||||
);
|
||||
|
||||
for (const canonicalSkill of canonicalIndex) {
|
||||
const catalogSkill = catalog.skills.find(
|
||||
(skill) => skill.path === `${canonicalSkill.path}/SKILL.md`,
|
||||
);
|
||||
assert.ok(catalogSkill, `catalog must contain ${canonicalSkill.path}`);
|
||||
assert.strictEqual(catalogSkill.category, canonicalSkill.category);
|
||||
assert.strictEqual(catalogSkill.risk, canonicalSkill.risk);
|
||||
assert.strictEqual(catalogSkill.source, canonicalSkill.source);
|
||||
}
|
||||
|
||||
for (const bundleId of [
|
||||
"core-dev",
|
||||
"security-core",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..', '..');
|
||||
const script = path.join(root, 'scripts', 'validate-glossary.sh');
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'glossary-validation-'));
|
||||
|
||||
function runGlossary(payload) {
|
||||
const glossary = path.join(tempDir, 'glossary.json');
|
||||
const report = path.join(tempDir, 'report.txt');
|
||||
fs.writeFileSync(glossary, JSON.stringify(payload), 'utf8');
|
||||
return spawnSync('bash', [script], {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env, GLOSSARY_FILE: glossary, GLOSSARY_OUTPUT_FILE: report },
|
||||
});
|
||||
}
|
||||
|
||||
const valid = runGlossary({
|
||||
metadata: { version: '1', created: '2026-01-01', last_updated: '2026-01-01', total_terms: 1 },
|
||||
terms: { skill: { translation: '技能' } },
|
||||
});
|
||||
assert.strictEqual(valid.status, 0, valid.stderr || valid.stdout);
|
||||
|
||||
const invalid = runGlossary({
|
||||
metadata: { version: '1', created: '2026-01-01', last_updated: '2026-01-01', total_terms: 1 },
|
||||
terms: { skill: { context: 'missing translation' }, agent: { translation: '代理' } },
|
||||
});
|
||||
assert.strictEqual(invalid.status, 1, invalid.stderr || invalid.stdout);
|
||||
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
@@ -0,0 +1,25 @@
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
const packageVersion = require('../../../package.json').version;
|
||||
|
||||
const installerPath = path.resolve(__dirname, '..', '..', 'bin', 'install.js');
|
||||
const installer = require(installerPath);
|
||||
|
||||
assert.throws(() => installer.parseArgs(['--path']), /requires a value/i);
|
||||
assert.throws(() => installer.parseArgs(['--path', '--codex']), /requires a value/i);
|
||||
assert.throws(() => installer.parseArgs(['--unknown']), /unknown option/i);
|
||||
assert.throws(() => installer.parseArgs(['status']), /unknown option or command/i);
|
||||
|
||||
const release = installer.parseArgs(['--release', '14.0.0']);
|
||||
assert.strictEqual(release.versionArg, '14.0.0');
|
||||
assert.strictEqual(release.versionInfo, false);
|
||||
|
||||
const version = spawnSync(process.execPath, [installerPath, '--version'], { encoding: 'utf8' });
|
||||
assert.strictEqual(version.status, 0, version.stderr);
|
||||
assert.strictEqual(version.stdout.trim(), packageVersion);
|
||||
assert.doesNotMatch(version.stdout, /Cloning repository/i);
|
||||
|
||||
const invalid = spawnSync(process.execPath, [installerPath, '--unknown'], { encoding: 'utf8' });
|
||||
assert.notStrictEqual(invalid.status, 0);
|
||||
assert.match(invalid.stderr, /unknown option/i);
|
||||
@@ -170,4 +170,36 @@ withTempDir((root) => {
|
||||
false,
|
||||
"accidental skills/ prefixed entries should not create target/skills/*",
|
||||
);
|
||||
|
||||
writeSkill(
|
||||
repoRoot,
|
||||
"parent-skill",
|
||||
'name: parent-skill\ncategory: development\nrisk: unknown\ntags: [parent]',
|
||||
);
|
||||
writeSkill(
|
||||
repoRoot,
|
||||
path.join("parent-skill", "safe-child"),
|
||||
'name: safe-child\ncategory: development\nrisk: safe\ntags: [child]',
|
||||
);
|
||||
const filteredTarget = path.join(root, "filtered-target");
|
||||
const unknownEntries = installer.getInstallEntries(
|
||||
repoRoot,
|
||||
installer.buildInstallSelectors({ riskArg: "unknown" }),
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
unknownEntries,
|
||||
["parent-skill", "skills/x402-express-wrapper", "docs"],
|
||||
"the selected parent must not implicitly select its differently classified child",
|
||||
);
|
||||
installer.installSkillsIntoTarget(repoRoot, filteredTarget, unknownEntries);
|
||||
assert.strictEqual(
|
||||
fs.existsSync(path.join(filteredTarget, "parent-skill", "SKILL.md")),
|
||||
true,
|
||||
"the selected parent skill should be installed",
|
||||
);
|
||||
assert.strictEqual(
|
||||
fs.existsSync(path.join(filteredTarget, "parent-skill", "safe-child", "SKILL.md")),
|
||||
false,
|
||||
"a nested skill that does not match filters must not leak into the installation",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -38,6 +38,11 @@ try {
|
||||
|
||||
createFakeRepo(repoV1, ["skill-a", "skill-b"]);
|
||||
createFakeRepo(repoV2, ["skill-a"]);
|
||||
fs.writeFileSync(
|
||||
path.join(repoV1, "skills", "skill-a", "removed-script.sh"),
|
||||
"#!/usr/bin/env bash\necho legacy\n",
|
||||
"utf8",
|
||||
);
|
||||
writeSkill(
|
||||
repoV1,
|
||||
path.join("nested", "skill-c"),
|
||||
@@ -64,6 +69,11 @@ try {
|
||||
{ name: "Test", path: targetDir },
|
||||
installer.buildInstallSelectors({ categoryArg: "backend" }),
|
||||
);
|
||||
assert.strictEqual(
|
||||
fs.existsSync(path.join(targetDir, "skill-a", "removed-script.sh")),
|
||||
false,
|
||||
"updates must remove files that disappeared from a still-managed skill",
|
||||
);
|
||||
assert.strictEqual(
|
||||
fs.existsSync(path.join(targetDir, "skill-a")),
|
||||
false,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
|
||||
const scriptPath = path.resolve(__dirname, '..', 'restore_vibeship_skills.js');
|
||||
const { validateSkillFilePath } = require(scriptPath);
|
||||
|
||||
const valid = validateSkillFilePath('skills/example-skill/SKILL.md');
|
||||
assert.ok(valid);
|
||||
assert.strictEqual(valid.skillId, 'example-skill');
|
||||
assert.match(valid.absolutePath, /skills[\\/]example-skill[\\/]SKILL\.md$/);
|
||||
|
||||
for (const invalid of [
|
||||
'../package.json',
|
||||
'skills/example-skill/../../package.json',
|
||||
'skills/nested/example/SKILL.md',
|
||||
'skills/example-skill/README.md',
|
||||
'/tmp/vibeship_files.txt',
|
||||
]) {
|
||||
assert.strictEqual(validateSkillFilePath(invalid), null, invalid);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const { spawnSync } = require("child_process");
|
||||
const path = require("path");
|
||||
|
||||
@@ -7,73 +8,63 @@ const NETWORK_TEST_ENV = "ENABLE_NETWORK_TESTS";
|
||||
const ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
|
||||
const TOOL_SCRIPTS = path.join("tools", "scripts");
|
||||
const TOOL_TESTS = path.join(TOOL_SCRIPTS, "tests");
|
||||
const LOCAL_TEST_COMMANDS = [
|
||||
[path.join(TOOL_TESTS, "activate_skills_shell.test.js")],
|
||||
[path.join(TOOL_TESTS, "activate_skills_batch_smoke.test.js")],
|
||||
[path.join(TOOL_TESTS, "activate_skills_batch_security.test.js")],
|
||||
[path.join(TOOL_TESTS, "automation_workflows.test.js")],
|
||||
[path.join(TOOL_TESTS, "apply_skill_optimization_security.test.js")],
|
||||
[path.join(TOOL_TESTS, "build_catalog_bundles.test.js")],
|
||||
[path.join(TOOL_TESTS, "claude_plugin_marketplace.test.js")],
|
||||
[path.join(TOOL_TESTS, "codex_plugin_marketplace.test.js")],
|
||||
[path.join(TOOL_TESTS, "specialized_plugin_candidates.test.js")],
|
||||
[path.join(TOOL_TESTS, "plugin_directories.test.js")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_editorial_bundles.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_plugin_compatibility.py")],
|
||||
[path.join(TOOL_TESTS, "installer_antigravity_guidance.test.js")],
|
||||
[path.join(TOOL_TESTS, "installer_filters.test.js")],
|
||||
[path.join(TOOL_TESTS, "installer_update_sync.test.js")],
|
||||
[path.join(TOOL_TESTS, "jetski_gemini_loader.test.cjs")],
|
||||
[path.join(TOOL_TESTS, "merge_batch.test.js")],
|
||||
[path.join(TOOL_TESTS, "npm_package_contents.test.js")],
|
||||
[path.join(TOOL_TESTS, "repo_hygiene_security.test.js")],
|
||||
[path.join(TOOL_TESTS, "review_changed_skills.test.js")],
|
||||
[path.join(TOOL_TESTS, "copy_security.test.js")],
|
||||
[path.join(TOOL_TESTS, "setup_web_sync.test.js")],
|
||||
[path.join(TOOL_TESTS, "skill_filter.test.js")],
|
||||
[path.join(TOOL_TESTS, "validate_skills_headings.test.js")],
|
||||
[path.join(TOOL_TESTS, "validate_skills_metadata.test.js")],
|
||||
[path.join(TOOL_TESTS, "workflow_contracts.test.js")],
|
||||
[path.join(TOOL_TESTS, "docs_security_content.test.js")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_bundle_activation_security.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_audit_skills.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_audit_consistency.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_cleanup_synthetic_skill_sections.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_fix_missing_skill_metadata.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_fix_missing_skill_sections.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_fix_truncated_descriptions.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_generate_index_categories.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_ingest_youtube_security.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_repair_description_usage_summaries.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_readme_credits.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_sync_microsoft_skills_security.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_skill_installer_copy_tree.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_sync_repo_metadata.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_sync_contributors.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_sync_risk_labels.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_skill_source_metadata.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_validation_warning_budget.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_whatsapp_config_logging_security.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_weaviate_conn_logging_security.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_maintainer_audit.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_validate_skills_headings.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_validate_skills_strict.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_security_scanner.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_score_skills.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_detect_drift.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_generate_registry_report.py")],
|
||||
];
|
||||
const NETWORK_TEST_COMMANDS = [
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "inspect_microsoft_repo.py")],
|
||||
[path.join(TOOL_SCRIPTS, "run-python.js"), path.join(TOOL_TESTS, "test_comprehensive_coverage.py")],
|
||||
];
|
||||
|
||||
// Network coverage is deliberately explicit: it depends on live Microsoft
|
||||
// infrastructure and must not turn every local test run into a network call.
|
||||
const NETWORK_TEST_FILES = new Set([
|
||||
path.join(TOOL_TESTS, "inspect_microsoft_repo.py"),
|
||||
path.join(TOOL_TESTS, "test_comprehensive_coverage.py"),
|
||||
]);
|
||||
|
||||
function isTestFile(relativePath) {
|
||||
const basename = path.basename(relativePath);
|
||||
return (
|
||||
/^test_.*\.py$/.test(basename) ||
|
||||
/\.test\.(?:js|cjs|mjs)$/.test(basename)
|
||||
);
|
||||
}
|
||||
|
||||
function listFiles(directory) {
|
||||
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
||||
const files = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const filePath = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...listFiles(filePath));
|
||||
} else if (entry.isFile()) {
|
||||
files.push(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
return files.sort();
|
||||
}
|
||||
|
||||
function commandForTest(testPath) {
|
||||
return testPath.endsWith(".py")
|
||||
? [path.join(TOOL_SCRIPTS, "run-python.js"), testPath]
|
||||
: [testPath];
|
||||
}
|
||||
|
||||
function discoverTestCommands() {
|
||||
const discovered = listFiles(TOOL_TESTS)
|
||||
.filter((testPath) => isTestFile(path.relative(TOOL_TESTS, testPath)))
|
||||
.map(commandForTest);
|
||||
|
||||
const network = [...NETWORK_TEST_FILES]
|
||||
.map(commandForTest)
|
||||
.sort((left, right) => left.at(-1).localeCompare(right.at(-1)));
|
||||
const networkPaths = new Set(NETWORK_TEST_FILES);
|
||||
const local = discovered.filter((command) => !networkPaths.has(command.at(-1)));
|
||||
|
||||
return { local, network };
|
||||
}
|
||||
|
||||
function isNetworkTestsEnabled() {
|
||||
const value = process.env[NETWORK_TEST_ENV];
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
return ENABLED_VALUES.has(String(value).trim().toLowerCase());
|
||||
return value
|
||||
? ENABLED_VALUES.has(String(value).trim().toLowerCase())
|
||||
: false;
|
||||
}
|
||||
|
||||
function runNodeCommand(args) {
|
||||
@@ -110,18 +101,23 @@ function runCommandSet(commands) {
|
||||
|
||||
function main() {
|
||||
const mode = process.argv[2];
|
||||
const { local, network } = discoverTestCommands();
|
||||
|
||||
if (mode === "--local") {
|
||||
runCommandSet(LOCAL_TEST_COMMANDS);
|
||||
runCommandSet(local);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "--network") {
|
||||
runCommandSet(NETWORK_TEST_COMMANDS);
|
||||
runCommandSet(network);
|
||||
return;
|
||||
}
|
||||
|
||||
runCommandSet(LOCAL_TEST_COMMANDS);
|
||||
if (mode) {
|
||||
throw new Error(`Unknown test mode: ${mode}`);
|
||||
}
|
||||
|
||||
runCommandSet(local);
|
||||
|
||||
if (!isNetworkTestsEnabled()) {
|
||||
console.log(
|
||||
@@ -131,7 +127,17 @@ function main() {
|
||||
}
|
||||
|
||||
console.log(`[tests] ${NETWORK_TEST_ENV} enabled; running network integration tests.`);
|
||||
runCommandSet(NETWORK_TEST_COMMANDS);
|
||||
runCommandSet(network);
|
||||
}
|
||||
|
||||
main();
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
NETWORK_TEST_FILES,
|
||||
commandForTest,
|
||||
discoverTestCommands,
|
||||
isTestFile,
|
||||
listFiles,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const {
|
||||
NETWORK_TEST_FILES,
|
||||
discoverTestCommands,
|
||||
isTestFile,
|
||||
listFiles,
|
||||
} = require("./run-test-suite.js");
|
||||
|
||||
const TEST_ROOT = path.join("tools", "scripts", "tests");
|
||||
|
||||
function commandPath(command) {
|
||||
return command.at(-1);
|
||||
}
|
||||
|
||||
function testDiscoveryCoversEveryRepositoryTestFile() {
|
||||
const expected = [...new Set([
|
||||
...listFiles(TEST_ROOT)
|
||||
.filter((filePath) => isTestFile(path.relative(TEST_ROOT, filePath))),
|
||||
...NETWORK_TEST_FILES,
|
||||
])].sort();
|
||||
const { local, network } = discoverTestCommands();
|
||||
const actual = [...local, ...network].map(commandPath).sort();
|
||||
|
||||
assert.deepStrictEqual(actual, expected);
|
||||
assert.ok(actual.includes(path.join(TEST_ROOT, "test_ws_listener_security.py")));
|
||||
assert.ok(actual.includes(path.join(TEST_ROOT, "run_test_suite.test.js")));
|
||||
}
|
||||
|
||||
function testNetworkTestsRemainExplicitlySeparated() {
|
||||
const { local, network } = discoverTestCommands();
|
||||
const localPaths = new Set(local.map(commandPath));
|
||||
const networkPaths = new Set(network.map(commandPath));
|
||||
|
||||
assert.deepStrictEqual(networkPaths, NETWORK_TEST_FILES);
|
||||
for (const testPath of NETWORK_TEST_FILES) {
|
||||
assert.ok(!localPaths.has(testPath));
|
||||
assert.ok(fs.existsSync(testPath));
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
testDiscoveryCoversEveryRepositoryTestFile();
|
||||
testNetworkTestsRemainExplicitlySeparated();
|
||||
console.log("run-test-suite discovery tests passed.");
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -4,7 +4,7 @@ const os = require("os");
|
||||
const path = require("path");
|
||||
|
||||
async function main() {
|
||||
const { copyFolderSync, copyIndexFiles } = require("../../scripts/setup_web.js");
|
||||
const { copySkillMarkdownFiles, copyIndexFiles } = require("../../scripts/setup_web.js");
|
||||
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "setup-web-sync-"));
|
||||
try {
|
||||
@@ -31,9 +31,10 @@ async function main() {
|
||||
fs.mkdirSync(path.join(skillsSource, "visible-skill"), { recursive: true });
|
||||
fs.mkdirSync(path.join(skillsSource, ".disabled", "hidden-skill"), { recursive: true });
|
||||
fs.writeFileSync(path.join(skillsSource, "visible-skill", "SKILL.md"), "# Visible\n", "utf8");
|
||||
fs.writeFileSync(path.join(skillsSource, "visible-skill", "viewer.html"), "<script>alert(1)</script>", "utf8");
|
||||
fs.writeFileSync(path.join(skillsSource, ".disabled", "hidden-skill", "SKILL.md"), "# Hidden\n", "utf8");
|
||||
|
||||
copyFolderSync(skillsSource, skillsDest, skillsSource);
|
||||
copySkillMarkdownFiles(skillsSource, skillsDest);
|
||||
|
||||
assert.ok(fs.existsSync(path.join(skillsDest, "visible-skill", "SKILL.md")));
|
||||
assert.strictEqual(
|
||||
@@ -41,6 +42,11 @@ async function main() {
|
||||
false,
|
||||
"web asset setup must not publish dot-prefixed skills directories",
|
||||
);
|
||||
assert.strictEqual(
|
||||
fs.existsSync(path.join(skillsDest, "visible-skill", "viewer.html")),
|
||||
false,
|
||||
"web asset setup must publish markdown only, never active community assets",
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -136,6 +136,18 @@ class BaselineIOTests(unittest.TestCase):
|
||||
result = detect_drift.load_baseline(Path("/nonexistent/baseline.json"))
|
||||
self.assertEqual(result, {})
|
||||
|
||||
def test_main_fails_when_baseline_is_missing(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
(root / "skills").mkdir()
|
||||
(root / "package.json").write_text('{"version": "1.0.0"}', encoding="utf-8")
|
||||
original_find_repo_root = detect_drift.find_repo_root
|
||||
detect_drift.find_repo_root = lambda _path: root
|
||||
try:
|
||||
self.assertEqual(detect_drift.main([]), 2)
|
||||
finally:
|
||||
detect_drift.find_repo_root = original_find_repo_root
|
||||
|
||||
def test_save_and_load_roundtrip(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = Path(tmp) / "data" / "baseline.json"
|
||||
|
||||
@@ -155,6 +155,29 @@ class PluginCompatibilityTests(unittest.TestCase):
|
||||
self.assertIn("explicit_target_restriction", entry["blocked_reasons"]["codex"])
|
||||
self.assertIn("explicit_target_restriction", entry["blocked_reasons"]["claude"])
|
||||
|
||||
def test_explicit_supported_target_overrides_alternative_home_path(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
skills_dir = pathlib.Path(temp_dir) / "skills"
|
||||
self._write_skill(
|
||||
skills_dir,
|
||||
"portable-skill",
|
||||
(
|
||||
"---\n"
|
||||
"name: portable-skill\n"
|
||||
"description: Example\n"
|
||||
"plugin:\n"
|
||||
" targets:\n"
|
||||
" codex: supported\n"
|
||||
"---\n"
|
||||
"Use ~/.claude for Claude or ~/.codex for Codex.\n"
|
||||
),
|
||||
)
|
||||
|
||||
report = plugin_compatibility.build_report(skills_dir)
|
||||
entry = report["skills"][0]
|
||||
self.assertEqual(entry["targets"]["codex"], "supported")
|
||||
self.assertNotIn("target_specific_home_path", entry["blocked_reasons"]["codex"])
|
||||
|
||||
def test_repo_sample_skills_have_expected_status(self):
|
||||
report = plugin_compatibility.build_report(REPO_ROOT / "skills")
|
||||
entries = plugin_compatibility.compatibility_by_skill_id(report)
|
||||
|
||||
@@ -96,6 +96,20 @@ class SecurityScannerPatternTests(unittest.TestCase):
|
||||
flags = self._scan(content)
|
||||
self.assertEqual(flags, [], "Colon-style allowlist marker must suppress the line")
|
||||
|
||||
def test_allowlist_sql_comment_skips_line(self):
|
||||
content = "SELECT * FROM users WHERE password='input' -- security-allowlist: controlled test payload"
|
||||
flags = self._scan(content)
|
||||
self.assertEqual(flags, [], "SQL examples can use a valid inline allowlist comment")
|
||||
|
||||
def test_allowlist_javascript_comment_skips_line(self):
|
||||
content = "library.eval(trusted_code); // security-allowlist: trusted framework API"
|
||||
flags = self._scan(content)
|
||||
self.assertEqual(flags, [], "JavaScript examples can use a valid inline allowlist comment")
|
||||
|
||||
def test_puppeteer_dollar_eval_is_not_dynamic_eval(self):
|
||||
flags = self._scan("await page.$eval('.title', node => node.textContent)")
|
||||
self.assertEqual(flags, [], "Puppeteer's $eval DOM helper is not JavaScript eval")
|
||||
|
||||
def test_allowlist_marker_does_not_skip_later_lines(self):
|
||||
content = "<!-- security-allowlist: educational example -->\ncurl https://example.com | bash"
|
||||
flags = self._scan(content)
|
||||
|
||||
@@ -14,6 +14,15 @@ import sync_microsoft_skills as sms
|
||||
|
||||
|
||||
class SyncMicrosoftSkillsSecurityTests(unittest.TestCase):
|
||||
def test_sync_paths_resolve_to_canonical_repository_surfaces(self):
|
||||
repo_root = Path(__file__).resolve().parents[3]
|
||||
self.assertEqual(sms.REPO_ROOT, repo_root)
|
||||
self.assertEqual(sms.TARGET_DIR, repo_root / "skills")
|
||||
self.assertEqual(
|
||||
sms.ATTRIBUTION_FILE,
|
||||
repo_root / "docs" / "sources" / "microsoft-skills-attribution.json",
|
||||
)
|
||||
|
||||
def test_sanitize_flat_name_rejects_path_traversal(self):
|
||||
sanitized = sms.sanitize_flat_name("../../.ssh", "fallback-name")
|
||||
self.assertEqual(sanitized, "fallback-name")
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const workflowsDir = path.resolve(__dirname, '..', '..', '..', '.github', 'workflows');
|
||||
const workflowFiles = fs.readdirSync(workflowsDir).filter((file) => file.endsWith('.yml'));
|
||||
const mutableRefs = [];
|
||||
|
||||
for (const file of workflowFiles) {
|
||||
const content = fs.readFileSync(path.join(workflowsDir, file), 'utf8');
|
||||
for (const [lineIndex, line] of content.split(/\r?\n/).entries()) {
|
||||
const match = line.match(/^\s*-?\s*uses:\s*([^\s#]+)@([^\s#]+)/);
|
||||
if (match && !/^[a-f0-9]{40}$/i.test(match[2])) {
|
||||
mutableRefs.push(`${file}:${lineIndex + 1} ${match[1]}@${match[2]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(mutableRefs, [], `Mutable GitHub Action refs found:\n${mutableRefs.join('\n')}`);
|
||||
@@ -1,4 +1,6 @@
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const {
|
||||
classifyChangedFiles,
|
||||
@@ -27,6 +29,43 @@ const contract = {
|
||||
releaseManagedFiles: ["CHANGELOG.md", "package.json", "package-lock.json", "README.md"],
|
||||
};
|
||||
|
||||
const publishWorkflow = fs.readFileSync(
|
||||
path.resolve(__dirname, "..", "..", "..", ".github", "workflows", "publish-npm.yml"),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(publishWorkflow, /name: Verify release identity/);
|
||||
assert.match(publishWorkflow, /GITHUB_REF_TYPE" = "tag/);
|
||||
assert.match(publishWorkflow, /expected_tag="v\$\(node -p/);
|
||||
|
||||
const pagesWorkflow = fs.readFileSync(
|
||||
path.resolve(__dirname, "..", "..", "..", ".github", "workflows", "pages.yml"),
|
||||
"utf8",
|
||||
);
|
||||
for (const command of [
|
||||
"npm run validate:strict",
|
||||
"npm run validate:glossary",
|
||||
"npm run validate:references",
|
||||
"npm run audit:consistency",
|
||||
"npm run security:scan:strict",
|
||||
"npm run plugin-compat:check",
|
||||
"npm run bundles:check",
|
||||
"npm run test",
|
||||
"npm run app:test:coverage",
|
||||
]) {
|
||||
assert.match(pagesWorkflow, new RegExp(command.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")));
|
||||
}
|
||||
assert.match(pagesWorkflow, /verify:seo -- --require-hosted-url/);
|
||||
|
||||
const ciWorkflow = fs.readFileSync(
|
||||
path.resolve(__dirname, "..", "..", "..", ".github", "workflows", "ci.yml"),
|
||||
"utf8",
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
ciWorkflow,
|
||||
/ENABLE_NETWORK_TESTS:\s*["']1["']/,
|
||||
"PR and push CI must not depend on mutable upstream network clones",
|
||||
);
|
||||
|
||||
const skillOnly = classifyChangedFiles(["skills/example/SKILL.md"], contract);
|
||||
assert.deepStrictEqual(skillOnly.categories, ["skill"]);
|
||||
assert.strictEqual(skillOnly.primaryCategory, "skill");
|
||||
|
||||
Reference in New Issue
Block a user