@raishin/vanguard-frontier-agentic 3.1.0 → 3.1.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +1 -1
- package/catalog/asset-integrity.json +18 -18
- package/package.json +3 -3
- package/plugins/vanguard-frontier-agentic/.codex-plugin/plugin.json +1 -1
- package/scripts/export-marketplace-agents.mjs +48 -15
- package/tests/test-vfa-export-coverage.test.mjs +30 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/github/copilot-cli/main/schemas/marketplace.schema.json",
|
|
3
3
|
"name": "vanguard-frontier-agentic",
|
|
4
4
|
"description": "Curated marketplace for cloud, zero-trust, and compliance-aware AI workflows — agents, skills, and rules spanning cloud, security, ERP, and platform providers.",
|
|
5
|
-
"version": "3.1.
|
|
5
|
+
"version": "3.1.1",
|
|
6
6
|
"owner": {
|
|
7
7
|
"name": "Raishin",
|
|
8
8
|
"url": "https://github.com/Raishin"
|
|
@@ -28968,7 +28968,7 @@
|
|
|
28968
28968
|
},
|
|
28969
28969
|
{
|
|
28970
28970
|
"tree": "scripts",
|
|
28971
|
-
"aggregate_sha256": "
|
|
28971
|
+
"aggregate_sha256": "ce5d7d297fec4a8fb096413d766d941200fe6568f0f7e1e6f947f473efcdeadc",
|
|
28972
28972
|
"files": [
|
|
28973
28973
|
{
|
|
28974
28974
|
"path": "scripts/apply-skill-allowed-tools.py",
|
|
@@ -28982,8 +28982,8 @@
|
|
|
28982
28982
|
},
|
|
28983
28983
|
{
|
|
28984
28984
|
"path": "scripts/export-marketplace-agents.mjs",
|
|
28985
|
-
"sha256": "
|
|
28986
|
-
"bytes":
|
|
28985
|
+
"sha256": "0624a3ae7d7aa250985dd5e3763a1f9266bc46722c7f5f7620f0859dccf38f37",
|
|
28986
|
+
"bytes": 28726
|
|
28987
28987
|
},
|
|
28988
28988
|
{
|
|
28989
28989
|
"path": "scripts/gen_azure_live_guards.py",
|
|
@@ -29385,7 +29385,7 @@
|
|
|
29385
29385
|
},
|
|
29386
29386
|
{
|
|
29387
29387
|
"tree": "plugins",
|
|
29388
|
-
"aggregate_sha256": "
|
|
29388
|
+
"aggregate_sha256": "9bcbf79b42023ffea07ac35e7d6a57f5b945c05afdced8cda7ae50cdcab53339",
|
|
29389
29389
|
"files": [
|
|
29390
29390
|
{
|
|
29391
29391
|
"path": "plugins/cross-platform-agent-template/.codex-plugin/plugin.json",
|
|
@@ -29399,7 +29399,7 @@
|
|
|
29399
29399
|
},
|
|
29400
29400
|
{
|
|
29401
29401
|
"path": "plugins/vanguard-frontier-agentic/.codex-plugin/plugin.json",
|
|
29402
|
-
"sha256": "
|
|
29402
|
+
"sha256": "4a5ac9ae2f5a81204afcb5c1ddd8157fd0866dc64bf4b6bcb519dc10192c4119",
|
|
29403
29403
|
"bytes": 1875
|
|
29404
29404
|
},
|
|
29405
29405
|
{
|
|
@@ -29416,7 +29416,7 @@
|
|
|
29416
29416
|
},
|
|
29417
29417
|
{
|
|
29418
29418
|
"tree": ".claude-plugin",
|
|
29419
|
-
"aggregate_sha256": "
|
|
29419
|
+
"aggregate_sha256": "a7c6e0414abf134bf22d1be821262175e1f23d942a4c8d238f91e9c902b792c4",
|
|
29420
29420
|
"files": [
|
|
29421
29421
|
{
|
|
29422
29422
|
"path": ".claude-plugin/README.md",
|
|
@@ -29425,19 +29425,19 @@
|
|
|
29425
29425
|
},
|
|
29426
29426
|
{
|
|
29427
29427
|
"path": ".claude-plugin/marketplace.json",
|
|
29428
|
-
"sha256": "
|
|
29428
|
+
"sha256": "6cac558ae8e51168149f96771c2ae1b350e7447b3583c16368d24f32fb4954aa",
|
|
29429
29429
|
"bytes": 834
|
|
29430
29430
|
},
|
|
29431
29431
|
{
|
|
29432
29432
|
"path": ".claude-plugin/plugin.json",
|
|
29433
|
-
"sha256": "
|
|
29433
|
+
"sha256": "dae34758b5252059c18d4d2c20ed6d16a09eb478541fa79985b3f27ab0ea7d30",
|
|
29434
29434
|
"bytes": 54712
|
|
29435
29435
|
}
|
|
29436
29436
|
]
|
|
29437
29437
|
},
|
|
29438
29438
|
{
|
|
29439
29439
|
"tree": ".cursor-plugin",
|
|
29440
|
-
"aggregate_sha256": "
|
|
29440
|
+
"aggregate_sha256": "ca39c896a869dd5f5a7c3c6bf2b630ed13fe4749eca55dc23fad24eeecdbbb95",
|
|
29441
29441
|
"files": [
|
|
29442
29442
|
{
|
|
29443
29443
|
"path": ".cursor-plugin/README.md",
|
|
@@ -29446,14 +29446,14 @@
|
|
|
29446
29446
|
},
|
|
29447
29447
|
{
|
|
29448
29448
|
"path": ".cursor-plugin/plugin.json",
|
|
29449
|
-
"sha256": "
|
|
29449
|
+
"sha256": "2363eda3be3e94f7aa2b0ebf13dc3de398862fe0c6c083a66566f8e9e9a911e7",
|
|
29450
29450
|
"bytes": 51718
|
|
29451
29451
|
}
|
|
29452
29452
|
]
|
|
29453
29453
|
},
|
|
29454
29454
|
{
|
|
29455
29455
|
"tree": ".github/plugin",
|
|
29456
|
-
"aggregate_sha256": "
|
|
29456
|
+
"aggregate_sha256": "0e0b126f84993b25cacefd2e3b2a0822a9373927af4e5780623e837eac9c673d",
|
|
29457
29457
|
"files": [
|
|
29458
29458
|
{
|
|
29459
29459
|
"path": ".github/plugin/README.md",
|
|
@@ -29462,7 +29462,7 @@
|
|
|
29462
29462
|
},
|
|
29463
29463
|
{
|
|
29464
29464
|
"path": ".github/plugin/marketplace.json",
|
|
29465
|
-
"sha256": "
|
|
29465
|
+
"sha256": "d6c094a33713c48cb3ea799d927b90168fb8c9f7f4e57089ec3cb2464b04b1c8",
|
|
29466
29466
|
"bytes": 770
|
|
29467
29467
|
}
|
|
29468
29468
|
]
|
|
@@ -29485,7 +29485,7 @@
|
|
|
29485
29485
|
},
|
|
29486
29486
|
{
|
|
29487
29487
|
"tree": "tests",
|
|
29488
|
-
"aggregate_sha256": "
|
|
29488
|
+
"aggregate_sha256": "f4056e5dacbb3c0e960409c6065a627d1cadc32ce69019a5183563328d6f0ce5",
|
|
29489
29489
|
"files": [
|
|
29490
29490
|
{
|
|
29491
29491
|
"path": "tests/AGENTS.md",
|
|
@@ -37059,8 +37059,8 @@
|
|
|
37059
37059
|
},
|
|
37060
37060
|
{
|
|
37061
37061
|
"path": "tests/test-vfa-export-coverage.test.mjs",
|
|
37062
|
-
"sha256": "
|
|
37063
|
-
"bytes":
|
|
37062
|
+
"sha256": "a054c8b1e0acc8e96da9f99f7442aa7396858e46d0894c8e6f0a010119c798ea",
|
|
37063
|
+
"bytes": 46776
|
|
37064
37064
|
},
|
|
37065
37065
|
{
|
|
37066
37066
|
"path": "tests/validate-agent-frontmatter-schema.py",
|
|
@@ -37213,8 +37213,8 @@
|
|
|
37213
37213
|
},
|
|
37214
37214
|
{
|
|
37215
37215
|
"path": "package.json",
|
|
37216
|
-
"sha256": "
|
|
37217
|
-
"bytes":
|
|
37216
|
+
"sha256": "3c58dd28c3ae02d94893f352843839c578d4583793841d042d8c2f9a25473afd",
|
|
37217
|
+
"bytes": 5934
|
|
37218
37218
|
},
|
|
37219
37219
|
{
|
|
37220
37220
|
"path": ".releaserc.js",
|
|
@@ -37222,5 +37222,5 @@
|
|
|
37222
37222
|
"bytes": 8340
|
|
37223
37223
|
}
|
|
37224
37224
|
],
|
|
37225
|
-
"aggregate_sha256": "
|
|
37225
|
+
"aggregate_sha256": "6b52cc932c71df7ec6143148489857d4f876392197c9026e529504cb899faead"
|
|
37226
37226
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raishin/vanguard-frontier-agentic",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Cloud and zero-trust agentic workflow marketplace for skills, agents, rules, MCP references, and compliance-aware architecture.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -85,10 +85,10 @@
|
|
|
85
85
|
"@semantic-release/commit-analyzer": "13.0.1",
|
|
86
86
|
"@semantic-release/exec": "7.1.0",
|
|
87
87
|
"@semantic-release/git": "10.0.1",
|
|
88
|
-
"@semantic-release/github": "12.0.
|
|
88
|
+
"@semantic-release/github": "12.0.9",
|
|
89
89
|
"@semantic-release/npm": "^13.1.5",
|
|
90
90
|
"@semantic-release/release-notes-generator": "14.1.1",
|
|
91
|
-
"conventional-changelog-conventionalcommits": "
|
|
91
|
+
"conventional-changelog-conventionalcommits": "10.2.1",
|
|
92
92
|
"fast-check": "^4.7.0",
|
|
93
93
|
"semantic-release": "25.0.5"
|
|
94
94
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanguard-frontier-agentic",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Curated marketplace for cloud and zero-trust AI workflows. 331 agents, 286 skills, and rules across AWS, Azure, OCI, GCP, Alibaba Cloud, Huawei Cloud, Kubernetes, and Terraform.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Raishin",
|
|
@@ -273,16 +273,6 @@ function ensurePlatform(platform) {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function assertWithin(parent, child, label) {
|
|
276
|
-
// Lexical containment check — path.resolve() is purely string-based and does
|
|
277
|
-
// NOT follow symlinks. This guards against traversal strings (../../) and
|
|
278
|
-
// metadata.json with absolute paths outside the repo.
|
|
279
|
-
//
|
|
280
|
-
// Residual TOCTOU: if an adversary races to create a symlink at `child`
|
|
281
|
-
// AFTER this check but BEFORE the actual write, the symlink target would be
|
|
282
|
-
// written to. copyFile()/copySkillTree() use lstatSync on source AND
|
|
283
|
-
// destination to detect pre-existing symlinks, which closes the window for
|
|
284
|
-
// the common case. A fully TOCTOU-proof solution requires O_NOFOLLOW at the
|
|
285
|
-
// kernel level, which is not exposed by Node.js fs APIs.
|
|
286
276
|
const resolvedParent = path.resolve(parent);
|
|
287
277
|
const resolvedChild = path.resolve(child);
|
|
288
278
|
const sep = path.sep;
|
|
@@ -295,6 +285,47 @@ function assertWithin(parent, child, label) {
|
|
|
295
285
|
}
|
|
296
286
|
}
|
|
297
287
|
|
|
288
|
+
function assertNoSymlinkAncestor(parent, child, label) {
|
|
289
|
+
assertWithin(parent, child, label);
|
|
290
|
+
|
|
291
|
+
const resolvedParent = path.resolve(parent);
|
|
292
|
+
const resolvedChild = path.resolve(child);
|
|
293
|
+
const relativeParent = path.dirname(path.relative(resolvedParent, resolvedChild));
|
|
294
|
+
if (!relativeParent || relativeParent === ".") return;
|
|
295
|
+
|
|
296
|
+
let current = resolvedParent;
|
|
297
|
+
for (const part of relativeParent.split(path.sep)) {
|
|
298
|
+
current = path.join(current, part);
|
|
299
|
+
let stat;
|
|
300
|
+
try {
|
|
301
|
+
stat = fs.lstatSync(current);
|
|
302
|
+
} catch (err) {
|
|
303
|
+
if (err && err.code === "ENOENT") return;
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
if (stat.isSymbolicLink()) {
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Refusing to ${label}: symbolic link ancestor '${current}' would redirect writes outside '${resolvedParent}'. ` +
|
|
309
|
+
`Remove the symlink and retry.`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
if (!stat.isDirectory()) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`Refusing to ${label}: path ancestor '${current}' is not a directory.`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function assertSafeWriteDestination(parent, destination, label) {
|
|
321
|
+
// Lexical containment blocks traversal strings and absolute-path metadata.
|
|
322
|
+
// The ancestor walk then rejects pre-existing symlinks such as `.codex -> /x`
|
|
323
|
+
// before mkdir/copy can follow them. Node does not expose fully race-proof
|
|
324
|
+
// O_NOFOLLOW directory creation, so this intentionally closes the reachable
|
|
325
|
+
// planted-symlink case without pretending to solve privileged TOCTOU races.
|
|
326
|
+
assertNoSymlinkAncestor(parent, destination, label);
|
|
327
|
+
}
|
|
328
|
+
|
|
298
329
|
function loadSkills() {
|
|
299
330
|
const skillsRoot = path.join(repoRoot, "skills");
|
|
300
331
|
if (!fs.existsSync(skillsRoot)) return new Map();
|
|
@@ -325,11 +356,12 @@ function loadSkills() {
|
|
|
325
356
|
return byName;
|
|
326
357
|
}
|
|
327
358
|
|
|
328
|
-
function copySkillTree(sourceDir, destDir, force) {
|
|
359
|
+
function copySkillTree(sourceDir, destDir, force, targetRoot) {
|
|
329
360
|
assertWithin(repoRoot, sourceDir, "read skill source");
|
|
330
361
|
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
331
362
|
const src = path.join(sourceDir, entry.name);
|
|
332
363
|
const dst = path.join(destDir, entry.name);
|
|
364
|
+
assertSafeWriteDestination(targetRoot, dst, "write skill tree destination");
|
|
333
365
|
if (entry.isSymbolicLink()) {
|
|
334
366
|
throw new Error(`Refusing to copy symbolic link in skill tree: ${src}`);
|
|
335
367
|
}
|
|
@@ -342,7 +374,7 @@ function copySkillTree(sourceDir, destDir, force) {
|
|
|
342
374
|
);
|
|
343
375
|
}
|
|
344
376
|
if (entry.isDirectory()) {
|
|
345
|
-
copySkillTree(src, dst, force);
|
|
377
|
+
copySkillTree(src, dst, force, targetRoot);
|
|
346
378
|
continue;
|
|
347
379
|
}
|
|
348
380
|
if (!entry.isFile()) continue;
|
|
@@ -404,7 +436,8 @@ function resolveCompanionSkills(selectedAgents, skillsByName, role, includeAll,
|
|
|
404
436
|
return { skillNames: [...skillNames].sort(), orphans };
|
|
405
437
|
}
|
|
406
438
|
|
|
407
|
-
function copyFile(source, destination, force) {
|
|
439
|
+
function copyFile(source, destination, force, targetRoot) {
|
|
440
|
+
assertSafeWriteDestination(targetRoot, destination, "write file destination");
|
|
408
441
|
const sourceStat = fs.lstatSync(source);
|
|
409
442
|
if (sourceStat.isSymbolicLink()) {
|
|
410
443
|
throw new Error(`Refusing to copy symbolic link as harness source: ${source}`);
|
|
@@ -662,7 +695,7 @@ function main() {
|
|
|
662
695
|
|
|
663
696
|
for (const operation of operations) {
|
|
664
697
|
assertWithin(args.repo, operation.dest, "write destination");
|
|
665
|
-
copyFile(operation.source, operation.dest, args.force);
|
|
698
|
+
copyFile(operation.source, operation.dest, args.force, args.repo);
|
|
666
699
|
if (platform === "codex") {
|
|
667
700
|
rewriteCodexAgentSkillPaths(operation.dest, args.repo);
|
|
668
701
|
}
|
|
@@ -705,7 +738,7 @@ function main() {
|
|
|
705
738
|
if (!sourceDir) continue;
|
|
706
739
|
const destDir = path.join(args.repo, skillsDestRoot, skillName);
|
|
707
740
|
assertWithin(args.repo, destDir, "write skill destination");
|
|
708
|
-
copySkillTree(sourceDir, destDir, args.force);
|
|
741
|
+
copySkillTree(sourceDir, destDir, args.force, args.repo);
|
|
709
742
|
console.log(`installed\tskill:${skillName}\t${platform}\t${path.relative(args.repo, destDir)}`);
|
|
710
743
|
bundled += 1;
|
|
711
744
|
}
|
|
@@ -864,6 +864,36 @@ const onDiskSkillNames = new Set(skillProviderByName.keys());
|
|
|
864
864
|
}
|
|
865
865
|
}
|
|
866
866
|
|
|
867
|
+
// F28c: codex export rejects symlinked platform directory ancestors.
|
|
868
|
+
{
|
|
869
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vfa-test-codex-parent-symlink-"));
|
|
870
|
+
const outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), "vfa-test-outside-"));
|
|
871
|
+
try {
|
|
872
|
+
fs.symlinkSync(outsideDir, path.join(tmpDir, ".codex"));
|
|
873
|
+
const r = run([
|
|
874
|
+
"--platform", "codex",
|
|
875
|
+
"--agents", "aws-iam-least-privilege-review-agent",
|
|
876
|
+
"--repo", tmpDir,
|
|
877
|
+
"--force",
|
|
878
|
+
]);
|
|
879
|
+
const outsideAgent = path.join(outsideDir, "agents", "aws-iam-least-privilege-review-agent.toml");
|
|
880
|
+
const outsideSkill = path.join(outsideDir, "skills", "aws-iam-least-privilege-review", "SKILL.md");
|
|
881
|
+
if (
|
|
882
|
+
r.exitCode !== 0 &&
|
|
883
|
+
/symbolic link ancestor/i.test(r.stderr) &&
|
|
884
|
+
!fs.existsSync(outsideAgent) &&
|
|
885
|
+
!fs.existsSync(outsideSkill)
|
|
886
|
+
) {
|
|
887
|
+
ok("F28c codex export rejects symlinked .codex ancestor without outside writes");
|
|
888
|
+
} else {
|
|
889
|
+
fail(`F28c expected symlink ancestor rejection without outside writes; exit=${r.exitCode} agentOutside=${fs.existsSync(outsideAgent)} skillOutside=${fs.existsSync(outsideSkill)} stderr=${r.stderr.slice(0, 300)}`);
|
|
890
|
+
}
|
|
891
|
+
} finally {
|
|
892
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
893
|
+
fs.rmSync(outsideDir, { recursive: true, force: true });
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
867
897
|
// F29: two-stage installer dry-run composes marketplace-safe export path.
|
|
868
898
|
{
|
|
869
899
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vfa-test-two-stage-"));
|