@aiclude/security-skill 2.1.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Skill Handler
3
- * Processes /security-scan skill invocations from Claude Code
3
+ * Claude Code의 /security-scan 스킬 호출을 처리합니다.
4
4
  * 서버 DB 조회 → 없으면 등록 → 스캔 결과 반환
5
5
  */
6
6
  interface SkillLookupInvocation {
@@ -11,8 +11,6 @@ interface SkillLookupInvocation {
11
11
  npmPackage?: string;
12
12
  }
13
13
  declare class SkillHandler {
14
- /** API 인증 헤더 생성 */
15
- private createAuthHeaders;
16
14
  /**
17
15
  * 보안 스캔 조회/등록
18
16
  * 기존 결과 검색 → 없으면 서버에 등록 → 스캔 실행 → 결과 반환
@@ -22,4 +20,36 @@ declare class SkillHandler {
22
20
  private formatScanSummary;
23
21
  }
24
22
 
25
- export { SkillHandler, type SkillLookupInvocation };
23
+ /**
24
+ * ASVS 시간 기반 동적 서명 생성
25
+ *
26
+ * MCP Server와 Skill이 동일하게 사용하는 인증 헬퍼.
27
+ * 고정 secret 없이, 타임스탬프에서 파생된 키로 HMAC 서명을 생성합니다.
28
+ *
29
+ * 동작 원리:
30
+ * 1. 타임스탬프를 분 단위로 양자화(quantize)하여 minuteSeed 생성
31
+ * 2. minuteSeed + 고정 salt("aiclude-vs")로 파생 키(derivedKey) 생성
32
+ * 3. "asvs:{source}:{name}:{minuteSeed}" 페이로드를 derivedKey로 HMAC-SHA256 서명
33
+ *
34
+ * 서버(BE)에서는 동일 로직으로 현재/이전 분 2개 윈도우를 비교하여 검증합니다.
35
+ * 이 방식으로 5분 이내의 clock drift를 허용합니다.
36
+ */
37
+ /**
38
+ * 시간 기반 동적 HMAC-SHA256 서명을 생성합니다.
39
+ *
40
+ * @param source - 요청 소스 ("mcp" | "skill" | "cli" | "ci")
41
+ * @param name - 타겟 이름
42
+ * @param timestamp - 밀리초 타임스탬프 (기본: 현재 시각)
43
+ * @returns 서명 문자열 (hex)
44
+ */
45
+ declare function createDynamicSignature(source: string, name: string, timestamp?: number): string;
46
+ /**
47
+ * API 요청에 필요한 인증 헤더를 생성합니다.
48
+ *
49
+ * @param source - 요청 소스 ("mcp" | "skill")
50
+ * @param name - 타겟 이름
51
+ * @returns HTTP 헤더 객체
52
+ */
53
+ declare function createAuthHeaders(source: string, name: string): Record<string, string>;
54
+
55
+ export { SkillHandler, type SkillLookupInvocation, createAuthHeaders, createDynamicSignature };
package/dist/index.js CHANGED
@@ -1,24 +1,33 @@
1
- // src/skill-handler.ts
1
+ // src/auth.ts
2
2
  import { createHmac } from "crypto";
3
- var API_BASE = "https://vs-api.aiclude.com";
4
- function createRequestSignature(name, timestamp) {
5
- const minuteSeed = Math.floor(Number(timestamp) / 6e4).toString(36);
6
- const payload = `asvs:${name}:${minuteSeed}`;
7
- const derivedKey = createHmac("sha256", minuteSeed).update("aiclude-vs").digest("hex").slice(0, 32);
8
- return createHmac("sha256", derivedKey).update(payload).digest("hex");
3
+ var SALT = "aiclude-vs";
4
+ var WINDOW_MS = 6e4;
5
+ function getMinuteSeed(timestamp) {
6
+ return Math.floor(timestamp / WINDOW_MS).toString(36);
7
+ }
8
+ function deriveKey(minuteSeed) {
9
+ return createHmac("sha256", minuteSeed).update(SALT).digest("hex").slice(0, 32);
10
+ }
11
+ function createDynamicSignature(source, name, timestamp = Date.now()) {
12
+ const minuteSeed = getMinuteSeed(timestamp);
13
+ const payload = `asvs:${source}:${name}:${minuteSeed}`;
14
+ const key = deriveKey(minuteSeed);
15
+ return createHmac("sha256", key).update(payload).digest("hex");
9
16
  }
17
+ function createAuthHeaders(source, name) {
18
+ const timestamp = Date.now();
19
+ const signature = createDynamicSignature(source, name, timestamp);
20
+ return {
21
+ "Content-Type": "application/json",
22
+ "X-ASVS-Source": source,
23
+ "X-ASVS-Timestamp": String(timestamp),
24
+ "X-ASVS-Signature": signature
25
+ };
26
+ }
27
+
28
+ // src/skill-handler.ts
29
+ var API_BASE = "https://vs-api.aiclude.com";
10
30
  var SkillHandler = class {
11
- /** API 인증 헤더 생성 */
12
- createAuthHeaders(name) {
13
- const timestamp = String(Date.now());
14
- const signature = createRequestSignature(name, timestamp);
15
- return {
16
- "Content-Type": "application/json",
17
- "X-ASVS-Source": "skill",
18
- "X-ASVS-Timestamp": timestamp,
19
- "X-ASVS-Signature": signature
20
- };
21
- }
22
31
  /**
23
32
  * 보안 스캔 조회/등록
24
33
  * 기존 결과 검색 → 없으면 서버에 등록 → 스캔 실행 → 결과 반환
@@ -27,7 +36,7 @@ var SkillHandler = class {
27
36
  const apiUrl = `${API_BASE}/api/v1/scan/lookup`;
28
37
  const lookupRes = await fetch(apiUrl, {
29
38
  method: "POST",
30
- headers: this.createAuthHeaders(invocation.name),
39
+ headers: createAuthHeaders("skill", invocation.name),
31
40
  body: JSON.stringify({
32
41
  name: invocation.name,
33
42
  type: invocation.type ?? "mcp-server",
@@ -56,7 +65,7 @@ var SkillHandler = class {
56
65
  while (Date.now() - startTime < maxWait) {
57
66
  await new Promise((r) => setTimeout(r, interval));
58
67
  const statusRes = await fetch(`${API_BASE}/api/v1/scan/status/${jobId}`, {
59
- headers: this.createAuthHeaders(invocation.name)
68
+ headers: createAuthHeaders("skill", invocation.name)
60
69
  });
61
70
  const statusData = await statusRes.json();
62
71
  if (statusData["status"] === "completed") {
@@ -198,5 +207,7 @@ var SkillHandler = class {
198
207
  }
199
208
  };
200
209
  export {
201
- SkillHandler
210
+ SkillHandler,
211
+ createAuthHeaders,
212
+ createDynamicSignature
202
213
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiclude/security-skill",
3
- "version": "2.1.1",
3
+ "version": "3.0.0",
4
4
  "description": "AICLUDE Security Vulnerability Scanner - Claude Code Skill for querying the AICLUDE scan database",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,9 +53,10 @@
53
53
  "clean": "rm -rf dist",
54
54
  "prepublishOnly": "npm run build"
55
55
  },
56
- "dependencies": {},
57
56
  "devDependencies": {
57
+ "@types/node": "^25.2.3",
58
58
  "tsup": "^8.0.0",
59
- "typescript": "^5.5.0"
59
+ "typescript": "^5.5.0",
60
+ "vitest": "^4.0.18"
60
61
  }
61
62
  }