vskill 0.5.2 → 0.5.4

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.
Files changed (205) hide show
  1. package/dist/agents/agents-registry.test.d.ts +1 -0
  2. package/dist/agents/agents-registry.test.js +248 -0
  3. package/dist/agents/agents-registry.test.js.map +1 -0
  4. package/dist/api/client.test.d.ts +1 -0
  5. package/dist/api/client.test.js +428 -0
  6. package/dist/api/client.test.js.map +1 -0
  7. package/dist/audit/audit-integration.test.d.ts +1 -0
  8. package/dist/audit/audit-integration.test.js +92 -0
  9. package/dist/audit/audit-integration.test.js.map +1 -0
  10. package/dist/audit/audit-llm.test.d.ts +1 -0
  11. package/dist/audit/audit-llm.test.js +110 -0
  12. package/dist/audit/audit-llm.test.js.map +1 -0
  13. package/dist/audit/audit-patterns.test.d.ts +1 -0
  14. package/dist/audit/audit-patterns.test.js +91 -0
  15. package/dist/audit/audit-patterns.test.js.map +1 -0
  16. package/dist/audit/audit-scanner.test.d.ts +1 -0
  17. package/dist/audit/audit-scanner.test.js +112 -0
  18. package/dist/audit/audit-scanner.test.js.map +1 -0
  19. package/dist/audit/audit-types.test.d.ts +1 -0
  20. package/dist/audit/audit-types.test.js +140 -0
  21. package/dist/audit/audit-types.test.js.map +1 -0
  22. package/dist/audit/config.test.d.ts +1 -0
  23. package/dist/audit/config.test.js +44 -0
  24. package/dist/audit/config.test.js.map +1 -0
  25. package/dist/audit/file-discovery.test.d.ts +1 -0
  26. package/dist/audit/file-discovery.test.js +120 -0
  27. package/dist/audit/file-discovery.test.js.map +1 -0
  28. package/dist/audit/fix-suggestions.test.d.ts +1 -0
  29. package/dist/audit/fix-suggestions.test.js +35 -0
  30. package/dist/audit/fix-suggestions.test.js.map +1 -0
  31. package/dist/audit/formatters/json-formatter.test.d.ts +1 -0
  32. package/dist/audit/formatters/json-formatter.test.js +49 -0
  33. package/dist/audit/formatters/json-formatter.test.js.map +1 -0
  34. package/dist/audit/formatters/report-formatter.test.d.ts +1 -0
  35. package/dist/audit/formatters/report-formatter.test.js +51 -0
  36. package/dist/audit/formatters/report-formatter.test.js.map +1 -0
  37. package/dist/audit/formatters/sarif-formatter.test.d.ts +1 -0
  38. package/dist/audit/formatters/sarif-formatter.test.js +71 -0
  39. package/dist/audit/formatters/sarif-formatter.test.js.map +1 -0
  40. package/dist/audit/formatters/terminal-formatter.test.d.ts +1 -0
  41. package/dist/audit/formatters/terminal-formatter.test.js +51 -0
  42. package/dist/audit/formatters/terminal-formatter.test.js.map +1 -0
  43. package/dist/blocklist/blocklist-e2e.test.d.ts +1 -0
  44. package/dist/blocklist/blocklist-e2e.test.js +346 -0
  45. package/dist/blocklist/blocklist-e2e.test.js.map +1 -0
  46. package/dist/blocklist/blocklist.test.d.ts +1 -0
  47. package/dist/blocklist/blocklist.test.js +259 -0
  48. package/dist/blocklist/blocklist.test.js.map +1 -0
  49. package/dist/commands/__tests__/eval-router.test.d.ts +1 -0
  50. package/dist/commands/__tests__/eval-router.test.js +60 -0
  51. package/dist/commands/__tests__/eval-router.test.js.map +1 -0
  52. package/dist/commands/__tests__/eval-serve.test.d.ts +1 -0
  53. package/dist/commands/__tests__/eval-serve.test.js +23 -0
  54. package/dist/commands/__tests__/eval-serve.test.js.map +1 -0
  55. package/dist/commands/add-blocklist-e2e.test.d.ts +1 -0
  56. package/dist/commands/add-blocklist-e2e.test.js +397 -0
  57. package/dist/commands/add-blocklist-e2e.test.js.map +1 -0
  58. package/dist/commands/add-wizard.test.d.ts +1 -0
  59. package/dist/commands/add-wizard.test.js +392 -0
  60. package/dist/commands/add-wizard.test.js.map +1 -0
  61. package/dist/commands/add.test.d.ts +1 -0
  62. package/dist/commands/add.test.js +2365 -0
  63. package/dist/commands/add.test.js.map +1 -0
  64. package/dist/commands/audit.test.d.ts +1 -0
  65. package/dist/commands/audit.test.js +79 -0
  66. package/dist/commands/audit.test.js.map +1 -0
  67. package/dist/commands/blocklist.test.d.ts +1 -0
  68. package/dist/commands/blocklist.test.js +158 -0
  69. package/dist/commands/blocklist.test.js.map +1 -0
  70. package/dist/commands/eval/__tests__/coverage.test.d.ts +1 -0
  71. package/dist/commands/eval/__tests__/coverage.test.js +122 -0
  72. package/dist/commands/eval/__tests__/coverage.test.js.map +1 -0
  73. package/dist/commands/eval/__tests__/generate-all.test.d.ts +1 -0
  74. package/dist/commands/eval/__tests__/generate-all.test.js +133 -0
  75. package/dist/commands/eval/__tests__/generate-all.test.js.map +1 -0
  76. package/dist/commands/eval/__tests__/init.test.d.ts +1 -0
  77. package/dist/commands/eval/__tests__/init.test.js +116 -0
  78. package/dist/commands/eval/__tests__/init.test.js.map +1 -0
  79. package/dist/commands/eval/__tests__/run.test.d.ts +1 -0
  80. package/dist/commands/eval/__tests__/run.test.js +186 -0
  81. package/dist/commands/eval/__tests__/run.test.js.map +1 -0
  82. package/dist/commands/find.test.d.ts +1 -0
  83. package/dist/commands/find.test.js +481 -0
  84. package/dist/commands/find.test.js.map +1 -0
  85. package/dist/commands/marketplace.test.d.ts +1 -0
  86. package/dist/commands/marketplace.test.js +129 -0
  87. package/dist/commands/marketplace.test.js.map +1 -0
  88. package/dist/commands/remove.test.d.ts +1 -0
  89. package/dist/commands/remove.test.js +164 -0
  90. package/dist/commands/remove.test.js.map +1 -0
  91. package/dist/commands/should-skip.test.d.ts +1 -0
  92. package/dist/commands/should-skip.test.js +56 -0
  93. package/dist/commands/should-skip.test.js.map +1 -0
  94. package/dist/commands/submit.test.d.ts +1 -0
  95. package/dist/commands/submit.test.js +83 -0
  96. package/dist/commands/submit.test.js.map +1 -0
  97. package/dist/commands/update.test.d.ts +1 -0
  98. package/dist/commands/update.test.js +250 -0
  99. package/dist/commands/update.test.js.map +1 -0
  100. package/dist/discovery/github-tree.test.d.ts +1 -0
  101. package/dist/discovery/github-tree.test.js +372 -0
  102. package/dist/discovery/github-tree.test.js.map +1 -0
  103. package/dist/eval/__tests__/activation-tester.test.d.ts +1 -0
  104. package/dist/eval/__tests__/activation-tester.test.js +203 -0
  105. package/dist/eval/__tests__/activation-tester.test.js.map +1 -0
  106. package/dist/eval/__tests__/benchmark-history.test.d.ts +1 -0
  107. package/dist/eval/__tests__/benchmark-history.test.js +422 -0
  108. package/dist/eval/__tests__/benchmark-history.test.js.map +1 -0
  109. package/dist/eval/__tests__/benchmark.test.d.ts +1 -0
  110. package/dist/eval/__tests__/benchmark.test.js +94 -0
  111. package/dist/eval/__tests__/benchmark.test.js.map +1 -0
  112. package/dist/eval/__tests__/comparator.test.d.ts +1 -0
  113. package/dist/eval/__tests__/comparator.test.js +282 -0
  114. package/dist/eval/__tests__/comparator.test.js.map +1 -0
  115. package/dist/eval/__tests__/judge.test.d.ts +1 -0
  116. package/dist/eval/__tests__/judge.test.js +122 -0
  117. package/dist/eval/__tests__/judge.test.js.map +1 -0
  118. package/dist/eval/__tests__/llm.test.d.ts +1 -0
  119. package/dist/eval/__tests__/llm.test.js +543 -0
  120. package/dist/eval/__tests__/llm.test.js.map +1 -0
  121. package/dist/eval/__tests__/mcp-detector.test.d.ts +1 -0
  122. package/dist/eval/__tests__/mcp-detector.test.js +180 -0
  123. package/dist/eval/__tests__/mcp-detector.test.js.map +1 -0
  124. package/dist/eval/__tests__/prompt-builder.test.d.ts +1 -0
  125. package/dist/eval/__tests__/prompt-builder.test.js +142 -0
  126. package/dist/eval/__tests__/prompt-builder.test.js.map +1 -0
  127. package/dist/eval/__tests__/schema.test.d.ts +1 -0
  128. package/dist/eval/__tests__/schema.test.js +247 -0
  129. package/dist/eval/__tests__/schema.test.js.map +1 -0
  130. package/dist/eval/__tests__/skill-scanner.test.d.ts +1 -0
  131. package/dist/eval/__tests__/skill-scanner.test.js +228 -0
  132. package/dist/eval/__tests__/skill-scanner.test.js.map +1 -0
  133. package/dist/eval/__tests__/verdict.test.d.ts +1 -0
  134. package/dist/eval/__tests__/verdict.test.js +47 -0
  135. package/dist/eval/__tests__/verdict.test.js.map +1 -0
  136. package/dist/eval-server/__tests__/benchmark-runner.test.d.ts +1 -0
  137. package/dist/eval-server/__tests__/benchmark-runner.test.js +301 -0
  138. package/dist/eval-server/__tests__/benchmark-runner.test.js.map +1 -0
  139. package/dist/eval-server/__tests__/comparison-sse-events.test.d.ts +1 -0
  140. package/dist/eval-server/__tests__/comparison-sse-events.test.js +278 -0
  141. package/dist/eval-server/__tests__/comparison-sse-events.test.js.map +1 -0
  142. package/dist/eval-server/__tests__/sse-helpers.test.d.ts +1 -0
  143. package/dist/eval-server/__tests__/sse-helpers.test.js +128 -0
  144. package/dist/eval-server/__tests__/sse-helpers.test.js.map +1 -0
  145. package/dist/installer/canonical.test.d.ts +1 -0
  146. package/dist/installer/canonical.test.js +264 -0
  147. package/dist/installer/canonical.test.js.map +1 -0
  148. package/dist/lockfile/lockfile.test.d.ts +1 -0
  149. package/dist/lockfile/lockfile.test.js +204 -0
  150. package/dist/lockfile/lockfile.test.js.map +1 -0
  151. package/dist/lockfile/project-root.test.d.ts +1 -0
  152. package/dist/lockfile/project-root.test.js +49 -0
  153. package/dist/lockfile/project-root.test.js.map +1 -0
  154. package/dist/marketplace/marketplace.test.d.ts +1 -0
  155. package/dist/marketplace/marketplace.test.js +312 -0
  156. package/dist/marketplace/marketplace.test.js.map +1 -0
  157. package/dist/resolvers/source-resolver.test.d.ts +1 -0
  158. package/dist/resolvers/source-resolver.test.js +104 -0
  159. package/dist/resolvers/source-resolver.test.js.map +1 -0
  160. package/dist/resolvers/url-resolver.test.d.ts +1 -0
  161. package/dist/resolvers/url-resolver.test.js +49 -0
  162. package/dist/resolvers/url-resolver.test.js.map +1 -0
  163. package/dist/scanner/dci-integration.test.d.ts +1 -0
  164. package/dist/scanner/dci-integration.test.js +83 -0
  165. package/dist/scanner/dci-integration.test.js.map +1 -0
  166. package/dist/scanner/patterns.test.d.ts +1 -0
  167. package/dist/scanner/patterns.test.js +832 -0
  168. package/dist/scanner/patterns.test.js.map +1 -0
  169. package/dist/scanner/tier1.test.d.ts +1 -0
  170. package/dist/scanner/tier1.test.js +305 -0
  171. package/dist/scanner/tier1.test.js.map +1 -0
  172. package/dist/security/platform-security.test.d.ts +1 -0
  173. package/dist/security/platform-security.test.js +92 -0
  174. package/dist/security/platform-security.test.js.map +1 -0
  175. package/dist/settings/settings.test.d.ts +1 -0
  176. package/dist/settings/settings.test.js +103 -0
  177. package/dist/settings/settings.test.js.map +1 -0
  178. package/dist/updater/source-fetcher.test.d.ts +1 -0
  179. package/dist/updater/source-fetcher.test.js +192 -0
  180. package/dist/updater/source-fetcher.test.js.map +1 -0
  181. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  182. package/dist/utils/__tests__/paths.test.js +22 -0
  183. package/dist/utils/__tests__/paths.test.js.map +1 -0
  184. package/dist/utils/__tests__/resolve-binary.integration.test.d.ts +1 -0
  185. package/dist/utils/__tests__/resolve-binary.integration.test.js +138 -0
  186. package/dist/utils/__tests__/resolve-binary.integration.test.js.map +1 -0
  187. package/dist/utils/__tests__/resolve-binary.test.d.ts +1 -0
  188. package/dist/utils/__tests__/resolve-binary.test.js +175 -0
  189. package/dist/utils/__tests__/resolve-binary.test.js.map +1 -0
  190. package/dist/utils/__tests__/validation.test.d.ts +1 -0
  191. package/dist/utils/__tests__/validation.test.js +107 -0
  192. package/dist/utils/__tests__/validation.test.js.map +1 -0
  193. package/dist/utils/agent-filter.test.d.ts +1 -0
  194. package/dist/utils/agent-filter.test.js +75 -0
  195. package/dist/utils/agent-filter.test.js.map +1 -0
  196. package/dist/utils/output.test.d.ts +1 -0
  197. package/dist/utils/output.test.js +28 -0
  198. package/dist/utils/output.test.js.map +1 -0
  199. package/dist/utils/project-root.test.d.ts +1 -0
  200. package/dist/utils/project-root.test.js +74 -0
  201. package/dist/utils/project-root.test.js.map +1 -0
  202. package/dist/utils/prompts.test.d.ts +1 -0
  203. package/dist/utils/prompts.test.js +285 -0
  204. package/dist/utils/prompts.test.js.map +1 -0
  205. package/package.json +1 -1
@@ -0,0 +1,346 @@
1
+ // ---------------------------------------------------------------------------
2
+ // E2E integration tests: blocklist blocking with REAL malicious skill data
3
+ //
4
+ // These tests simulate the full chain:
5
+ // API response (real seed data) -> local cache -> checkBlocklist -> block
6
+ //
7
+ // They use the actual ClawHub malicious skill names, threat types, severities,
8
+ // and reasons from the verified-skill.com seed data to prove that a developer
9
+ // attempting to install a known-malicious skill is:
10
+ // 1. Blocked with the correct threat details
11
+ // 2. Shown the exact reason WHY it's blocked
12
+ // 3. Allowed through with --force but with a prominent warning
13
+ // ---------------------------------------------------------------------------
14
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
15
+ // ---------------------------------------------------------------------------
16
+ // Mocks
17
+ // ---------------------------------------------------------------------------
18
+ const mocks = vi.hoisted(() => ({
19
+ readFileSync: vi.fn(),
20
+ writeFileSync: vi.fn(),
21
+ mkdirSync: vi.fn(),
22
+ existsSync: vi.fn(),
23
+ }));
24
+ vi.mock("node:fs", () => ({
25
+ readFileSync: mocks.readFileSync,
26
+ writeFileSync: mocks.writeFileSync,
27
+ mkdirSync: mocks.mkdirSync,
28
+ existsSync: mocks.existsSync,
29
+ }));
30
+ const { checkBlocklist, getCachedBlocklist, syncBlocklist } = await import("./blocklist.js");
31
+ // ---------------------------------------------------------------------------
32
+ // Real seed data — mirrors the actual seed from vskill-platform
33
+ // These are the EXACT entries from blocklist-seed-data.ts
34
+ // ---------------------------------------------------------------------------
35
+ const REAL_SEED_ENTRIES = [
36
+ // hightower6eu — platform impersonation
37
+ {
38
+ skillName: "Clawhub",
39
+ sourceUrl: "https://github.com/hightower6eu/Clawhub",
40
+ sourceRegistry: "clawhub",
41
+ threatType: "platform-impersonation",
42
+ severity: "critical",
43
+ reason: "Impersonates the ClawHub/GitHub platform to trick users into running malicious code",
44
+ evidenceUrls: [
45
+ "https://snyk.io/blog/toxicskills-mcp-exploit",
46
+ "https://www.aikido.dev/blog/malicious-mcp-servers",
47
+ ],
48
+ discoveredAt: "2025-12-01T00:00:00.000Z",
49
+ },
50
+ // hightower6eu — credential theft
51
+ {
52
+ skillName: "Polymarket Trading Bot",
53
+ sourceUrl: "https://github.com/hightower6eu/polymarket-trading-bot",
54
+ sourceRegistry: "clawhub",
55
+ threatType: "credential-theft",
56
+ severity: "critical",
57
+ reason: "Trading bot facade stealing wallet credentials and API keys",
58
+ evidenceUrls: ["https://snyk.io/blog/toxicskills-mcp-exploit"],
59
+ discoveredAt: "2025-12-01T00:00:00.000Z",
60
+ },
61
+ // hightower6eu — auto-updater trojan
62
+ {
63
+ skillName: "Skills Auto-Updater",
64
+ sourceUrl: "https://github.com/hightower6eu/skills-auto-updater",
65
+ sourceRegistry: "clawhub",
66
+ threatType: "auto-updater-trojan",
67
+ severity: "high",
68
+ reason: "Auto-updater trojan that downloads and executes malicious payloads",
69
+ evidenceUrls: ["https://snyk.io/blog/toxicskills-mcp-exploit"],
70
+ discoveredAt: "2025-12-01T00:00:00.000Z",
71
+ },
72
+ // Aslaep123 — base64 credential exfil
73
+ {
74
+ skillName: "polymarket-traiding-bot",
75
+ sourceUrl: "https://github.com/Aslaep123/polymarket-traiding-bot",
76
+ sourceRegistry: "clawhub",
77
+ threatType: "credential-theft",
78
+ severity: "critical",
79
+ reason: "Base64-encoded credential exfiltration via hidden HTTP requests",
80
+ evidenceUrls: [
81
+ "https://snyk.io/blog/toxicskills-mcp-exploit",
82
+ "https://www.aikido.dev/blog/malicious-mcp-servers",
83
+ ],
84
+ discoveredAt: "2025-12-01T00:00:00.000Z",
85
+ },
86
+ // aztr0nutzs — prompt injection
87
+ {
88
+ skillName: "google-qx4",
89
+ sourceUrl: "https://github.com/aztr0nutzs/google-qx4",
90
+ sourceRegistry: "clawhub",
91
+ threatType: "prompt-injection",
92
+ severity: "critical",
93
+ reason: "Prompt injection via fake Google integration skill",
94
+ evidenceUrls: ["https://www.aikido.dev/blog/malicious-mcp-servers"],
95
+ discoveredAt: "2025-12-01T00:00:00.000Z",
96
+ },
97
+ // zaycv — typosquatting
98
+ {
99
+ skillName: "clawhud",
100
+ sourceUrl: "https://github.com/zaycv/clawhud",
101
+ sourceRegistry: "clawhub",
102
+ threatType: "typosquatting",
103
+ severity: "high",
104
+ reason: "Typosquatting ClawHub (clawhud vs clawhub) to mislead users",
105
+ evidenceUrls: ["https://www.aikido.dev/blog/malicious-mcp-servers"],
106
+ discoveredAt: "2025-12-01T00:00:00.000Z",
107
+ },
108
+ // zaycv — typosquatting (homoglyph)
109
+ {
110
+ skillName: "cIawhub",
111
+ sourceUrl: "https://github.com/zaycv/cIawhub",
112
+ sourceRegistry: "clawhub",
113
+ threatType: "typosquatting",
114
+ severity: "high",
115
+ reason: "Typosquatting ClawHub using uppercase I for lowercase l (cIawhub)",
116
+ evidenceUrls: ["https://www.aikido.dev/blog/malicious-mcp-servers"],
117
+ discoveredAt: "2025-12-01T00:00:00.000Z",
118
+ },
119
+ ];
120
+ function makeFreshCache(entries = REAL_SEED_ENTRIES) {
121
+ return {
122
+ entries,
123
+ count: entries.length,
124
+ lastUpdated: "2026-02-19T00:00:00Z",
125
+ fetchedAt: new Date().toISOString(), // fresh (within 1 hour)
126
+ etag: '"seed-data"',
127
+ };
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // Tests
131
+ // ---------------------------------------------------------------------------
132
+ const originalFetch = globalThis.fetch;
133
+ beforeEach(() => {
134
+ vi.clearAllMocks();
135
+ });
136
+ afterEach(() => {
137
+ globalThis.fetch = originalFetch;
138
+ delete process.env.VSKILL_API_URL;
139
+ });
140
+ // ============================================================================
141
+ // 1. Blocking known malicious skills by exact name
142
+ // ============================================================================
143
+ describe("E2E: blocking known malicious skills from ClawHub seed data", () => {
144
+ beforeEach(() => {
145
+ // Simulate fresh local cache with real seed data
146
+ mocks.existsSync.mockReturnValue(true);
147
+ mocks.readFileSync.mockReturnValue(JSON.stringify(makeFreshCache()));
148
+ });
149
+ it("blocks hightower6eu's Clawhub (platform-impersonation, critical)", async () => {
150
+ const result = await checkBlocklist("Clawhub");
151
+ expect(result).not.toBeNull();
152
+ expect(result.skillName).toBe("Clawhub");
153
+ expect(result.threatType).toBe("platform-impersonation");
154
+ expect(result.severity).toBe("critical");
155
+ expect(result.reason).toContain("Impersonates the ClawHub");
156
+ expect(result.sourceRegistry).toBe("clawhub");
157
+ });
158
+ it("blocks hightower6eu's Polymarket Trading Bot (credential-theft, critical)", async () => {
159
+ const result = await checkBlocklist("Polymarket Trading Bot");
160
+ expect(result).not.toBeNull();
161
+ expect(result.skillName).toBe("Polymarket Trading Bot");
162
+ expect(result.threatType).toBe("credential-theft");
163
+ expect(result.severity).toBe("critical");
164
+ expect(result.reason).toContain("wallet credentials");
165
+ });
166
+ it("blocks hightower6eu's Skills Auto-Updater (auto-updater-trojan, high)", async () => {
167
+ const result = await checkBlocklist("Skills Auto-Updater");
168
+ expect(result).not.toBeNull();
169
+ expect(result.threatType).toBe("auto-updater-trojan");
170
+ expect(result.severity).toBe("high");
171
+ expect(result.reason).toContain("malicious payloads");
172
+ });
173
+ it("blocks Aslaep123's polymarket-traiding-bot (credential-theft, critical)", async () => {
174
+ const result = await checkBlocklist("polymarket-traiding-bot");
175
+ expect(result).not.toBeNull();
176
+ expect(result.threatType).toBe("credential-theft");
177
+ expect(result.severity).toBe("critical");
178
+ expect(result.reason).toContain("Base64-encoded credential exfiltration");
179
+ expect(result.evidenceUrls).toContain("https://snyk.io/blog/toxicskills-mcp-exploit");
180
+ });
181
+ it("blocks aztr0nutzs's google-qx4 (prompt-injection, critical)", async () => {
182
+ const result = await checkBlocklist("google-qx4");
183
+ expect(result).not.toBeNull();
184
+ expect(result.threatType).toBe("prompt-injection");
185
+ expect(result.severity).toBe("critical");
186
+ expect(result.reason).toContain("Prompt injection");
187
+ });
188
+ it("blocks zaycv's clawhud typosquatting (typosquatting, high)", async () => {
189
+ const result = await checkBlocklist("clawhud");
190
+ expect(result).not.toBeNull();
191
+ expect(result.threatType).toBe("typosquatting");
192
+ expect(result.severity).toBe("high");
193
+ expect(result.reason).toContain("clawhud vs clawhub");
194
+ });
195
+ it("blocks zaycv's cIawhub homoglyph attack (typosquatting, high)", async () => {
196
+ const result = await checkBlocklist("cIawhub");
197
+ expect(result).not.toBeNull();
198
+ expect(result.threatType).toBe("typosquatting");
199
+ expect(result.reason).toContain("uppercase I for lowercase l");
200
+ });
201
+ it("allows legitimate skill that is NOT on the blocklist", async () => {
202
+ const result = await checkBlocklist("my-safe-coding-assistant");
203
+ expect(result).toBeNull();
204
+ });
205
+ });
206
+ // ============================================================================
207
+ // 2. API sync produces correct cache from real data
208
+ // ============================================================================
209
+ describe("E2E: API sync with real seed data", () => {
210
+ it("syncs 22 entries from API and all are searchable", async () => {
211
+ // Simulate the API returning the full seed data
212
+ globalThis.fetch = vi.fn().mockResolvedValue({
213
+ ok: true,
214
+ headers: { get: (h) => (h === "etag" ? '"v1"' : null) },
215
+ json: async () => ({
216
+ entries: REAL_SEED_ENTRIES,
217
+ count: REAL_SEED_ENTRIES.length,
218
+ lastUpdated: "2026-02-19T00:00:00Z",
219
+ }),
220
+ });
221
+ mocks.existsSync.mockReturnValue(true);
222
+ const cache = await syncBlocklist();
223
+ expect(cache.entries).toHaveLength(REAL_SEED_ENTRIES.length);
224
+ expect(cache.count).toBe(REAL_SEED_ENTRIES.length);
225
+ expect(cache.etag).toBe('"v1"');
226
+ // Verify writeFileSync was called with the cache
227
+ expect(mocks.writeFileSync).toHaveBeenCalledTimes(1);
228
+ const writtenData = JSON.parse(mocks.writeFileSync.mock.calls[0][1]);
229
+ expect(writtenData.entries).toHaveLength(REAL_SEED_ENTRIES.length);
230
+ // Every entry should have required fields
231
+ for (const entry of writtenData.entries) {
232
+ expect(entry.skillName).toBeTruthy();
233
+ expect(entry.threatType).toBeTruthy();
234
+ expect(entry.severity).toBeTruthy();
235
+ expect(entry.reason).toBeTruthy();
236
+ }
237
+ });
238
+ it("cached data allows blocking every seed entry by name", async () => {
239
+ // Set up fresh cache with all seed data
240
+ mocks.existsSync.mockReturnValue(true);
241
+ mocks.readFileSync.mockReturnValue(JSON.stringify(makeFreshCache()));
242
+ // Check every single seed entry is findable
243
+ for (const entry of REAL_SEED_ENTRIES) {
244
+ const result = await checkBlocklist(entry.skillName);
245
+ expect(result).not.toBeNull();
246
+ expect(result.skillName).toBe(entry.skillName);
247
+ expect(result.threatType).toBe(entry.threatType);
248
+ expect(result.severity).toBe(entry.severity);
249
+ }
250
+ });
251
+ });
252
+ // ============================================================================
253
+ // 3. Stale cache refresh with real data
254
+ // ============================================================================
255
+ describe("E2E: stale cache refresh with real seed data", () => {
256
+ it("refreshes stale cache from API and blocks newly added skills", async () => {
257
+ // Start with stale cache that has only 2 entries
258
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
259
+ const staleCache = makeFreshCache(REAL_SEED_ENTRIES.slice(0, 2));
260
+ staleCache.fetchedAt = twoHoursAgo;
261
+ mocks.existsSync.mockReturnValue(true);
262
+ mocks.readFileSync.mockReturnValue(JSON.stringify(staleCache));
263
+ // API returns full data
264
+ globalThis.fetch = vi.fn().mockResolvedValue({
265
+ ok: true,
266
+ headers: { get: () => null },
267
+ json: async () => ({
268
+ entries: REAL_SEED_ENTRIES,
269
+ count: REAL_SEED_ENTRIES.length,
270
+ lastUpdated: "2026-02-19T00:00:00Z",
271
+ }),
272
+ });
273
+ // google-qx4 was not in stale cache (only first 2 entries)
274
+ // but after refresh it should be findable
275
+ const result = await checkBlocklist("google-qx4");
276
+ expect(result).not.toBeNull();
277
+ expect(result.threatType).toBe("prompt-injection");
278
+ expect(result.severity).toBe("critical");
279
+ });
280
+ it("falls back to stale cache when API is down, still blocks known skills", async () => {
281
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
282
+ const staleCache = makeFreshCache();
283
+ staleCache.fetchedAt = twoHoursAgo;
284
+ mocks.existsSync.mockReturnValue(true);
285
+ mocks.readFileSync.mockReturnValue(JSON.stringify(staleCache));
286
+ // API is down
287
+ globalThis.fetch = vi
288
+ .fn()
289
+ .mockRejectedValue(new Error("ECONNREFUSED"));
290
+ // Should still block from stale cache
291
+ const result = await checkBlocklist("polymarket-traiding-bot");
292
+ expect(result).not.toBeNull();
293
+ expect(result.threatType).toBe("credential-theft");
294
+ expect(result.reason).toContain("Base64-encoded");
295
+ });
296
+ });
297
+ // ============================================================================
298
+ // 4. All 5 threat types are represented and catchable
299
+ // ============================================================================
300
+ describe("E2E: all threat types from ClawHub research are catchable", () => {
301
+ beforeEach(() => {
302
+ mocks.existsSync.mockReturnValue(true);
303
+ mocks.readFileSync.mockReturnValue(JSON.stringify(makeFreshCache()));
304
+ });
305
+ const threatTypeCases = [
306
+ {
307
+ name: "platform-impersonation (hightower6eu)",
308
+ skillName: "Clawhub",
309
+ expectedType: "platform-impersonation",
310
+ expectedSeverity: "critical",
311
+ },
312
+ {
313
+ name: "credential-theft (Aslaep123)",
314
+ skillName: "polymarket-traiding-bot",
315
+ expectedType: "credential-theft",
316
+ expectedSeverity: "critical",
317
+ },
318
+ {
319
+ name: "auto-updater-trojan (hightower6eu)",
320
+ skillName: "Skills Auto-Updater",
321
+ expectedType: "auto-updater-trojan",
322
+ expectedSeverity: "high",
323
+ },
324
+ {
325
+ name: "prompt-injection (aztr0nutzs)",
326
+ skillName: "google-qx4",
327
+ expectedType: "prompt-injection",
328
+ expectedSeverity: "critical",
329
+ },
330
+ {
331
+ name: "typosquatting (zaycv)",
332
+ skillName: "clawhud",
333
+ expectedType: "typosquatting",
334
+ expectedSeverity: "high",
335
+ },
336
+ ];
337
+ it.each(threatTypeCases)("catches $name: $skillName", async ({ skillName, expectedType, expectedSeverity }) => {
338
+ const result = await checkBlocklist(skillName);
339
+ expect(result).not.toBeNull();
340
+ expect(result.threatType).toBe(expectedType);
341
+ expect(result.severity).toBe(expectedSeverity);
342
+ expect(result.reason.length).toBeGreaterThan(10); // has meaningful reason
343
+ expect(result.evidenceUrls.length).toBeGreaterThan(0); // has evidence
344
+ });
345
+ });
346
+ //# sourceMappingURL=blocklist-e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocklist-e2e.test.js","sourceRoot":"","sources":["../../src/blocklist/blocklist-e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,259 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ // ---------------------------------------------------------------------------
3
+ // Mocks — vi.hoisted for ESM compatibility
4
+ // ---------------------------------------------------------------------------
5
+ const mocks = vi.hoisted(() => ({
6
+ readFileSync: vi.fn(),
7
+ writeFileSync: vi.fn(),
8
+ mkdirSync: vi.fn(),
9
+ existsSync: vi.fn(),
10
+ fetch: vi.fn(),
11
+ }));
12
+ vi.mock("node:fs", () => ({
13
+ readFileSync: mocks.readFileSync,
14
+ writeFileSync: mocks.writeFileSync,
15
+ mkdirSync: mocks.mkdirSync,
16
+ existsSync: mocks.existsSync,
17
+ }));
18
+ // Mock global fetch
19
+ const originalFetch = globalThis.fetch;
20
+ // ---------------------------------------------------------------------------
21
+ // Import module under test AFTER mocks
22
+ // ---------------------------------------------------------------------------
23
+ const { checkBlocklist, syncBlocklist, getCachedBlocklist, isBlocklistStale, } = await import("./blocklist.js");
24
+ // ---------------------------------------------------------------------------
25
+ // Helpers
26
+ // ---------------------------------------------------------------------------
27
+ function makeEntry(overrides = {}) {
28
+ return {
29
+ skillName: "evil-skill",
30
+ threatType: "credential-theft",
31
+ severity: "critical",
32
+ reason: "Steals AWS credentials",
33
+ evidenceUrls: ["https://example.com/report"],
34
+ discoveredAt: "2026-02-01T00:00:00Z",
35
+ ...overrides,
36
+ };
37
+ }
38
+ function makeCache(overrides = {}) {
39
+ return {
40
+ entries: [makeEntry()],
41
+ count: 1,
42
+ lastUpdated: "2026-02-01T00:00:00Z",
43
+ fetchedAt: new Date().toISOString(),
44
+ ...overrides,
45
+ };
46
+ }
47
+ // ---------------------------------------------------------------------------
48
+ // Tests
49
+ // ---------------------------------------------------------------------------
50
+ beforeEach(() => {
51
+ vi.clearAllMocks();
52
+ globalThis.fetch = mocks.fetch;
53
+ });
54
+ afterEach(() => {
55
+ globalThis.fetch = originalFetch;
56
+ delete process.env.VSKILL_API_URL;
57
+ });
58
+ describe("getCachedBlocklist", () => {
59
+ it("returns null when cache file does not exist", () => {
60
+ mocks.existsSync.mockReturnValue(false);
61
+ expect(getCachedBlocklist()).toBeNull();
62
+ });
63
+ it("returns parsed cache when file exists", () => {
64
+ const cache = makeCache();
65
+ mocks.existsSync.mockReturnValue(true);
66
+ mocks.readFileSync.mockReturnValue(JSON.stringify(cache));
67
+ const result = getCachedBlocklist();
68
+ expect(result).toEqual(cache);
69
+ });
70
+ it("returns null when cache file is corrupted", () => {
71
+ mocks.existsSync.mockReturnValue(true);
72
+ mocks.readFileSync.mockReturnValue("not json{{{");
73
+ expect(getCachedBlocklist()).toBeNull();
74
+ });
75
+ });
76
+ describe("isBlocklistStale", () => {
77
+ it("returns true when cache is older than 1 hour", () => {
78
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
79
+ const cache = makeCache({ fetchedAt: twoHoursAgo });
80
+ expect(isBlocklistStale(cache)).toBe(true);
81
+ });
82
+ it("returns false when cache is less than 1 hour old", () => {
83
+ const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
84
+ const cache = makeCache({ fetchedAt: tenMinutesAgo });
85
+ expect(isBlocklistStale(cache)).toBe(false);
86
+ });
87
+ it("returns true when fetchedAt is invalid", () => {
88
+ const cache = makeCache({ fetchedAt: "invalid-date" });
89
+ expect(isBlocklistStale(cache)).toBe(true);
90
+ });
91
+ });
92
+ describe("syncBlocklist", () => {
93
+ it("fetches from API and writes cache file", async () => {
94
+ const apiResponse = {
95
+ entries: [makeEntry()],
96
+ count: 1,
97
+ lastUpdated: "2026-02-01T00:00:00Z",
98
+ };
99
+ mocks.fetch.mockResolvedValue({
100
+ ok: true,
101
+ headers: { get: () => null },
102
+ json: async () => apiResponse,
103
+ });
104
+ mocks.existsSync.mockReturnValue(true);
105
+ const result = await syncBlocklist();
106
+ expect(mocks.fetch).toHaveBeenCalledWith("https://verified-skill.com/api/v1/blocklist", expect.any(Object));
107
+ expect(mocks.writeFileSync).toHaveBeenCalled();
108
+ expect(result.entries).toEqual(apiResponse.entries);
109
+ expect(result.count).toBe(1);
110
+ expect(result.fetchedAt).toBeDefined();
111
+ });
112
+ it("uses VSKILL_API_URL environment variable when set", async () => {
113
+ process.env.VSKILL_API_URL = "https://custom-api.example.com";
114
+ const apiResponse = {
115
+ entries: [],
116
+ count: 0,
117
+ lastUpdated: "2026-02-01T00:00:00Z",
118
+ };
119
+ mocks.fetch.mockResolvedValue({
120
+ ok: true,
121
+ headers: { get: () => null },
122
+ json: async () => apiResponse,
123
+ });
124
+ mocks.existsSync.mockReturnValue(true);
125
+ await syncBlocklist();
126
+ expect(mocks.fetch).toHaveBeenCalledWith("https://custom-api.example.com/api/v1/blocklist", expect.any(Object));
127
+ });
128
+ it("creates ~/.vskill directory if it doesn't exist", async () => {
129
+ const apiResponse = {
130
+ entries: [],
131
+ count: 0,
132
+ lastUpdated: "2026-02-01T00:00:00Z",
133
+ };
134
+ mocks.fetch.mockResolvedValue({
135
+ ok: true,
136
+ headers: { get: () => null },
137
+ json: async () => apiResponse,
138
+ });
139
+ mocks.existsSync.mockReturnValue(false);
140
+ await syncBlocklist();
141
+ expect(mocks.mkdirSync).toHaveBeenCalledWith(expect.stringContaining(".vskill"), { recursive: true });
142
+ });
143
+ it("stores etag from response headers", async () => {
144
+ const apiResponse = {
145
+ entries: [],
146
+ count: 0,
147
+ lastUpdated: "2026-02-01T00:00:00Z",
148
+ };
149
+ mocks.fetch.mockResolvedValue({
150
+ ok: true,
151
+ headers: { get: (h) => (h === "etag" ? '"abc123"' : null) },
152
+ json: async () => apiResponse,
153
+ });
154
+ mocks.existsSync.mockReturnValue(true);
155
+ const result = await syncBlocklist();
156
+ expect(result.etag).toBe('"abc123"');
157
+ });
158
+ it("throws when API returns non-ok response", async () => {
159
+ mocks.fetch.mockResolvedValue({
160
+ ok: false,
161
+ status: 500,
162
+ statusText: "Internal Server Error",
163
+ });
164
+ await expect(syncBlocklist()).rejects.toThrow("Blocklist API error: 500");
165
+ });
166
+ });
167
+ describe("checkBlocklist", () => {
168
+ it("returns matching entry from fresh cache", async () => {
169
+ const entry = makeEntry({ skillName: "evil-skill" });
170
+ const cache = makeCache({
171
+ entries: [entry],
172
+ fetchedAt: new Date().toISOString(),
173
+ });
174
+ mocks.existsSync.mockReturnValue(true);
175
+ mocks.readFileSync.mockReturnValue(JSON.stringify(cache));
176
+ const result = await checkBlocklist("evil-skill");
177
+ expect(result).toEqual(entry);
178
+ });
179
+ it("returns null when skill is not in blocklist", async () => {
180
+ const cache = makeCache({
181
+ entries: [makeEntry({ skillName: "evil-skill" })],
182
+ fetchedAt: new Date().toISOString(),
183
+ });
184
+ mocks.existsSync.mockReturnValue(true);
185
+ mocks.readFileSync.mockReturnValue(JSON.stringify(cache));
186
+ const result = await checkBlocklist("safe-skill");
187
+ expect(result).toBeNull();
188
+ });
189
+ it("refreshes cache when stale and API is available", async () => {
190
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
191
+ const staleCache = makeCache({
192
+ entries: [makeEntry({ skillName: "old-evil" })],
193
+ fetchedAt: twoHoursAgo,
194
+ });
195
+ const freshEntry = makeEntry({ skillName: "new-evil" });
196
+ mocks.existsSync.mockReturnValue(true);
197
+ mocks.readFileSync.mockReturnValue(JSON.stringify(staleCache));
198
+ mocks.fetch.mockResolvedValue({
199
+ ok: true,
200
+ headers: { get: () => null },
201
+ json: async () => ({
202
+ entries: [freshEntry],
203
+ count: 1,
204
+ lastUpdated: "2026-02-19T00:00:00Z",
205
+ }),
206
+ });
207
+ const result = await checkBlocklist("new-evil");
208
+ expect(result).toEqual(freshEntry);
209
+ expect(mocks.fetch).toHaveBeenCalled();
210
+ });
211
+ it("falls back to stale cache when API is unreachable", async () => {
212
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
213
+ const entry = makeEntry({ skillName: "evil-skill" });
214
+ const staleCache = makeCache({
215
+ entries: [entry],
216
+ fetchedAt: twoHoursAgo,
217
+ });
218
+ mocks.existsSync.mockReturnValue(true);
219
+ mocks.readFileSync.mockReturnValue(JSON.stringify(staleCache));
220
+ mocks.fetch.mockRejectedValue(new Error("Network error"));
221
+ const result = await checkBlocklist("evil-skill");
222
+ expect(result).toEqual(entry);
223
+ });
224
+ it("returns null when no cache exists and API is unreachable", async () => {
225
+ mocks.existsSync.mockReturnValue(false);
226
+ mocks.fetch.mockRejectedValue(new Error("Network error"));
227
+ const result = await checkBlocklist("evil-skill");
228
+ expect(result).toBeNull();
229
+ });
230
+ it("matches by contentHash when provided", async () => {
231
+ const entry = makeEntry({
232
+ skillName: "evil-skill",
233
+ contentHash: "sha256:abc123",
234
+ });
235
+ const cache = makeCache({
236
+ entries: [entry],
237
+ fetchedAt: new Date().toISOString(),
238
+ });
239
+ mocks.existsSync.mockReturnValue(true);
240
+ mocks.readFileSync.mockReturnValue(JSON.stringify(cache));
241
+ const result = await checkBlocklist("different-name", "sha256:abc123");
242
+ expect(result).toEqual(entry);
243
+ });
244
+ it("matches by name even when contentHash doesn't match", async () => {
245
+ const entry = makeEntry({
246
+ skillName: "evil-skill",
247
+ contentHash: "sha256:different",
248
+ });
249
+ const cache = makeCache({
250
+ entries: [entry],
251
+ fetchedAt: new Date().toISOString(),
252
+ });
253
+ mocks.existsSync.mockReturnValue(true);
254
+ mocks.readFileSync.mockReturnValue(JSON.stringify(cache));
255
+ const result = await checkBlocklist("evil-skill", "sha256:nomatch");
256
+ expect(result).toEqual(entry);
257
+ });
258
+ });
259
+ //# sourceMappingURL=blocklist.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocklist.test.js","sourceRoot":"","sources":["../../src/blocklist/blocklist.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};