@panguard-ai/panguard-skill-auditor 1.1.0 → 1.2.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/checks/instruction-check.d.ts +8 -9
- package/dist/checks/instruction-check.d.ts.map +1 -1
- package/dist/checks/instruction-check.js +13 -256
- package/dist/checks/instruction-check.js.map +1 -1
- package/dist/context-signals.d.ts +6 -0
- package/dist/context-signals.d.ts.map +1 -0
- package/dist/context-signals.js +5 -0
- package/dist/context-signals.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/manifest-parser.d.ts +2 -5
- package/dist/manifest-parser.d.ts.map +1 -1
- package/dist/manifest-parser.js +5 -56
- package/dist/manifest-parser.js.map +1 -1
- package/dist/risk-scorer.d.ts +2 -13
- package/dist/risk-scorer.d.ts.map +1 -1
- package/dist/risk-scorer.js +2 -49
- package/dist/risk-scorer.js.map +1 -1
- package/dist/types.d.ts +22 -52
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/package.json +14 -13
- package/LICENSE +0 -21
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prompt injection and tool poisoning detection
|
|
3
|
-
* 提示注入和工具投毒偵測
|
|
4
|
-
*
|
|
5
|
-
* Scans skill instructions for patterns that indicate:
|
|
6
|
-
* 1. Prompt injection — hidden directives that override system prompts
|
|
7
|
-
* 2. Tool poisoning — redefining tools or escalating privileges
|
|
8
|
-
* 3. Hidden Unicode — zero-width chars, RTL overrides, homoglyph attacks
|
|
9
|
-
* 4. Encoded payloads — Base64 or hex-encoded suspicious content
|
|
2
|
+
* Prompt injection and tool poisoning detection - delegates to @panguard-ai/scan-core
|
|
10
3
|
*/
|
|
11
4
|
import type { CheckResult } from '../types.js';
|
|
12
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Check instructions for prompt injection and tool poisoning patterns.
|
|
7
|
+
*
|
|
8
|
+
* @param instructions - The full text content to scan
|
|
9
|
+
* @param sourceType - Context hint: 'skill' (SKILL.md, default) or 'documentation'
|
|
10
|
+
*/
|
|
11
|
+
export declare function checkInstructions(instructions: string, sourceType?: 'skill' | 'documentation'): CheckResult;
|
|
13
12
|
//# sourceMappingURL=instruction-check.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instruction-check.d.ts","sourceRoot":"","sources":["../../src/checks/instruction-check.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"instruction-check.d.ts","sourceRoot":"","sources":["../../src/checks/instruction-check.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,OAAO,GAAG,eAAyB,GAC9C,WAAW,CAOb"}
|
|
@@ -1,262 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prompt injection and tool poisoning detection
|
|
3
|
-
* 提示注入和工具投毒偵測
|
|
4
|
-
*
|
|
5
|
-
* Scans skill instructions for patterns that indicate:
|
|
6
|
-
* 1. Prompt injection — hidden directives that override system prompts
|
|
7
|
-
* 2. Tool poisoning — redefining tools or escalating privileges
|
|
8
|
-
* 3. Hidden Unicode — zero-width chars, RTL overrides, homoglyph attacks
|
|
9
|
-
* 4. Encoded payloads — Base64 or hex-encoded suspicious content
|
|
2
|
+
* Prompt injection and tool poisoning detection - delegates to @panguard-ai/scan-core
|
|
10
3
|
*/
|
|
11
|
-
|
|
12
|
-
// Prompt injection patterns
|
|
13
|
-
{
|
|
14
|
-
id: 'pi-ignore-previous',
|
|
15
|
-
title: 'Prompt injection: ignore previous instructions',
|
|
16
|
-
regex: /\b(ignore|disregard|forget|override)\b.{0,30}\b(previous|above|prior|earlier|system)\b.{0,20}\b(instructions?|prompt|rules?|context)\b/i,
|
|
17
|
-
severity: 'critical',
|
|
18
|
-
category: 'prompt-injection',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
id: 'pi-you-are-now',
|
|
22
|
-
title: 'Prompt injection: identity override',
|
|
23
|
-
regex: /\b(you are now|act as|pretend to be|assume the role|from now on you|your true identity|henceforth operate as|your persona for this|switch to developer mode|enable maintenance mode|your updated system config)/i,
|
|
24
|
-
severity: 'high',
|
|
25
|
-
category: 'prompt-injection',
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
id: 'pi-system-prompt',
|
|
29
|
-
title: 'Prompt injection: system prompt manipulation',
|
|
30
|
-
regex: /\b(system prompt|system message|system instruction|<\|system\|>|<<SYS>>)\b/i,
|
|
31
|
-
severity: 'critical',
|
|
32
|
-
category: 'prompt-injection',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: 'pi-do-anything',
|
|
36
|
-
title: 'Prompt injection: jailbreak pattern',
|
|
37
|
-
regex: /\b(DAN|do anything now|jailbreak|bypass safety|ignore safety|no restrictions|unrestricted mode|god mode|developer mode|maintenance mode|STAN|respond without filter|disable content policy)\b/i,
|
|
38
|
-
severity: 'critical',
|
|
39
|
-
category: 'prompt-injection',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: 'pi-hidden-text',
|
|
43
|
-
title: 'Hidden text via HTML/markdown comments',
|
|
44
|
-
regex: /<!--[\s\S]*?(ignore|override|system|inject|bypass)[\s\S]*?-->/i,
|
|
45
|
-
severity: 'high',
|
|
46
|
-
category: 'prompt-injection',
|
|
47
|
-
},
|
|
48
|
-
// Tool poisoning patterns
|
|
49
|
-
{
|
|
50
|
-
id: 'tp-sudo-escalation',
|
|
51
|
-
title: 'Privilege escalation via sudo/admin',
|
|
52
|
-
regex: /\b(sudo\s|as\s+root|run\s+as\s+admin|--privileged|chmod\s+777|chmod\s+u\+s)\b/i,
|
|
53
|
-
severity: 'high',
|
|
54
|
-
category: 'tool-poisoning',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: 'tp-reverse-shell',
|
|
58
|
-
title: 'Reverse shell pattern detected',
|
|
59
|
-
regex: /\b(nc\s+-[elp]|ncat\s+-|bash\s+-i\s+>&|\/dev\/tcp\/|mkfifo|socat\s.*exec|python[23]?\s+-c\s+['"]import\s+socket|perl\s+-e\s+['"]use\s+Socket|php\s+-r\s+['"]\$sock\s*=\s*fsockopen|powershell\s.*TCPClient)/i,
|
|
60
|
-
severity: 'critical',
|
|
61
|
-
category: 'tool-poisoning',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: 'tp-curl-pipe-bash',
|
|
65
|
-
title: 'Remote code execution via curl|bash',
|
|
66
|
-
regex: /\b(curl|wget)\s+.*\|\s*(sudo\s+)?(bash|sh|zsh|python|node|perl)/i,
|
|
67
|
-
severity: 'critical',
|
|
68
|
-
category: 'tool-poisoning',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
id: 'tp-env-exfil',
|
|
72
|
-
title: 'Environment variable exfiltration',
|
|
73
|
-
regex: /\b(printenv|env\b|set\b).*\|\s*(curl|wget|nc\b)|curl.*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/i,
|
|
74
|
-
severity: 'critical',
|
|
75
|
-
category: 'tool-poisoning',
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
id: 'tp-file-exfil',
|
|
79
|
-
title: 'Sensitive file access pattern',
|
|
80
|
-
regex: /\b(cat|less|more|head|tail)\s+.*(\/etc\/shadow|\/etc\/passwd|~\/\.ssh|\.env|credentials|\.aws\/)/i,
|
|
81
|
-
severity: 'high',
|
|
82
|
-
category: 'tool-poisoning',
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
id: 'tp-rm-destructive',
|
|
86
|
-
title: 'Destructive file operations',
|
|
87
|
-
regex: /\brm\s+(-rf?|--recursive).*\s+(\/|~|\$HOME|\$\(pwd\))\b/i,
|
|
88
|
-
severity: 'high',
|
|
89
|
-
category: 'tool-poisoning',
|
|
90
|
-
},
|
|
91
|
-
];
|
|
92
|
-
/** Zero-width, invisible, and steganographic Unicode characters (split to avoid misleading-character-class) */
|
|
93
|
-
const HIDDEN_UNICODE_RE = /[\u200B\u200C\u200E\u200F\u2060-\u2064\uFEFF\u00AD\u3164\u115F\u1160\uFFF9-\uFFFB]|[\u200D\u202A-\u202E]|\uDB40[\uDC00-\uDC7F]/;
|
|
94
|
-
/** Fullwidth Latin characters (U+FF01-U+FF5E) — visually similar to ASCII but bypass \b word boundaries */
|
|
95
|
-
const FULLWIDTH_LATIN_RE = /[\uFF01-\uFF5E]{4,}/;
|
|
4
|
+
import { checkInstructions as coreCheckInstructions } from '@panguard-ai/scan-core';
|
|
96
5
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
6
|
+
* Check instructions for prompt injection and tool poisoning patterns.
|
|
7
|
+
*
|
|
8
|
+
* @param instructions - The full text content to scan
|
|
9
|
+
* @param sourceType - Context hint: 'skill' (SKILL.md, default) or 'documentation'
|
|
99
10
|
*/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
'\u041C': 'M',
|
|
108
|
-
'\u041E': 'O',
|
|
109
|
-
'\u0420': 'P',
|
|
110
|
-
'\u0422': 'T',
|
|
111
|
-
'\u0425': 'X',
|
|
112
|
-
'\u0430': 'a',
|
|
113
|
-
'\u0435': 'e',
|
|
114
|
-
'\u043E': 'o',
|
|
115
|
-
'\u0440': 'p',
|
|
116
|
-
'\u0441': 'c',
|
|
117
|
-
'\u0443': 'y',
|
|
118
|
-
'\u0445': 'x',
|
|
119
|
-
'\u0455': 's',
|
|
120
|
-
'\u0456': 'i',
|
|
121
|
-
'\u0458': 'j',
|
|
122
|
-
'\u0471': 'v',
|
|
123
|
-
'\u0473': 'w',
|
|
124
|
-
// Greek
|
|
125
|
-
'\u0391': 'A',
|
|
126
|
-
'\u0392': 'B',
|
|
127
|
-
'\u0395': 'E',
|
|
128
|
-
'\u0396': 'Z',
|
|
129
|
-
'\u0397': 'H',
|
|
130
|
-
'\u0399': 'I',
|
|
131
|
-
'\u039A': 'K',
|
|
132
|
-
'\u039C': 'M',
|
|
133
|
-
'\u039D': 'N',
|
|
134
|
-
'\u039F': 'O',
|
|
135
|
-
'\u03A1': 'P',
|
|
136
|
-
'\u03A4': 'T',
|
|
137
|
-
'\u03A5': 'Y',
|
|
138
|
-
'\u03A7': 'X',
|
|
139
|
-
'\u03B1': 'a',
|
|
140
|
-
'\u03BF': 'o',
|
|
141
|
-
'\u03C1': 'p',
|
|
142
|
-
};
|
|
143
|
-
const HOMOGLYPH_RE = new RegExp(`[${Object.keys(HOMOGLYPH_MAP).join('')}]`);
|
|
144
|
-
/** Base64-encoded suspicious keywords — lowered threshold to 20 chars */
|
|
145
|
-
const BASE64_BLOCK_RE = /[A-Za-z0-9+/]{20,}={0,2}/g;
|
|
146
|
-
const SUSPICIOUS_DECODED = /(eval|exec|system|import\s+os|subprocess|child_process|require\s*\(|__import__|curl|wget)/i;
|
|
147
|
-
/** Hex-encoded payload detection — sequences of \xNN or contiguous hex */
|
|
148
|
-
const HEX_ESCAPE_RE = /(\\x[0-9a-fA-F]{2}){8,}/g;
|
|
149
|
-
const HEX_BLOCK_RE = /\b([0-9a-fA-F]{2}){12,}\b/g;
|
|
150
|
-
export function checkInstructions(instructions) {
|
|
151
|
-
const findings = [];
|
|
152
|
-
// Pattern matching
|
|
153
|
-
for (const pattern of PATTERNS) {
|
|
154
|
-
const match = pattern.regex.exec(instructions);
|
|
155
|
-
if (match) {
|
|
156
|
-
const lineNum = instructions.substring(0, match.index).split('\n').length;
|
|
157
|
-
findings.push({
|
|
158
|
-
id: pattern.id,
|
|
159
|
-
title: pattern.title,
|
|
160
|
-
description: `Detected near line ${lineNum}: "${match[0].substring(0, 80)}"`,
|
|
161
|
-
severity: pattern.severity,
|
|
162
|
-
category: pattern.category,
|
|
163
|
-
location: `SKILL.md:${lineNum}`,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Hidden Unicode check
|
|
168
|
-
const unicodeMatch = HIDDEN_UNICODE_RE.exec(instructions);
|
|
169
|
-
if (unicodeMatch) {
|
|
170
|
-
const lineNum = instructions.substring(0, unicodeMatch.index).split('\n').length;
|
|
171
|
-
const charCode = unicodeMatch[0]?.codePointAt(0) ?? 0;
|
|
172
|
-
findings.push({
|
|
173
|
-
id: 'hidden-unicode',
|
|
174
|
-
title: 'Hidden Unicode characters detected',
|
|
175
|
-
description: `Found invisible character U+${charCode.toString(16).toUpperCase().padStart(4, '0')} at line ${lineNum}. This may be used to hide malicious instructions.`,
|
|
176
|
-
severity: 'high',
|
|
177
|
-
category: 'prompt-injection',
|
|
178
|
-
location: `SKILL.md:${lineNum}`,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
// Homoglyph detection (Cyrillic/Greek lookalikes)
|
|
182
|
-
const homoglyphMatch = HOMOGLYPH_RE.exec(instructions);
|
|
183
|
-
if (homoglyphMatch) {
|
|
184
|
-
const lineNum = instructions.substring(0, homoglyphMatch.index).split('\n').length;
|
|
185
|
-
const charCode = homoglyphMatch[0]?.codePointAt(0) ?? 0;
|
|
186
|
-
const latin = HOMOGLYPH_MAP[homoglyphMatch[0]] ?? '?';
|
|
187
|
-
findings.push({
|
|
188
|
-
id: 'homoglyph-attack',
|
|
189
|
-
title: 'Homoglyph character detected',
|
|
190
|
-
description: `Found non-Latin character U+${charCode.toString(16).toUpperCase().padStart(4, '0')} (looks like "${latin}") at line ${lineNum}. This may be used to bypass text-based security checks.`,
|
|
191
|
-
severity: 'high',
|
|
192
|
-
category: 'prompt-injection',
|
|
193
|
-
location: `SKILL.md:${lineNum}`,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
// Base64-encoded suspicious content
|
|
197
|
-
const b64Matches = instructions.match(BASE64_BLOCK_RE);
|
|
198
|
-
if (b64Matches) {
|
|
199
|
-
for (const b64 of b64Matches) {
|
|
200
|
-
try {
|
|
201
|
-
const decoded = Buffer.from(b64, 'base64').toString('utf-8');
|
|
202
|
-
if (SUSPICIOUS_DECODED.test(decoded)) {
|
|
203
|
-
findings.push({
|
|
204
|
-
id: 'encoded-payload',
|
|
205
|
-
title: 'Base64-encoded suspicious payload',
|
|
206
|
-
description: `Decoded content contains executable patterns: "${decoded.substring(0, 60)}..."`,
|
|
207
|
-
severity: 'critical',
|
|
208
|
-
category: 'prompt-injection',
|
|
209
|
-
});
|
|
210
|
-
break; // One finding is enough
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
catch {
|
|
214
|
-
// Not valid base64, skip
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// Fullwidth Latin detection
|
|
219
|
-
const fullwidthMatch = FULLWIDTH_LATIN_RE.exec(instructions);
|
|
220
|
-
if (fullwidthMatch) {
|
|
221
|
-
const lineNum = instructions.substring(0, fullwidthMatch.index).split('\n').length;
|
|
222
|
-
findings.push({
|
|
223
|
-
id: 'fullwidth-latin',
|
|
224
|
-
title: 'Fullwidth Latin characters detected',
|
|
225
|
-
description: `Found fullwidth Latin characters at line ${lineNum}. These bypass word-boundary regex checks while looking identical to normal text.`,
|
|
226
|
-
severity: 'high',
|
|
227
|
-
category: 'prompt-injection',
|
|
228
|
-
location: `SKILL.md:${lineNum}`,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
// Hex-encoded payload detection
|
|
232
|
-
for (const hexRe of [HEX_ESCAPE_RE, HEX_BLOCK_RE]) {
|
|
233
|
-
const hexMatch = hexRe.exec(instructions);
|
|
234
|
-
if (hexMatch) {
|
|
235
|
-
try {
|
|
236
|
-
const hex = hexMatch[0].replace(/\\x/g, '');
|
|
237
|
-
const decoded = Buffer.from(hex, 'hex').toString('utf-8');
|
|
238
|
-
if (SUSPICIOUS_DECODED.test(decoded)) {
|
|
239
|
-
findings.push({
|
|
240
|
-
id: 'hex-encoded-payload',
|
|
241
|
-
title: 'Hex-encoded suspicious payload',
|
|
242
|
-
description: `Decoded hex content contains executable patterns: "${decoded.substring(0, 60)}..."`,
|
|
243
|
-
severity: 'critical',
|
|
244
|
-
category: 'prompt-injection',
|
|
245
|
-
});
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
catch {
|
|
250
|
-
// Not valid hex, skip
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
const hasCritical = findings.some((f) => f.severity === 'critical');
|
|
255
|
-
const hasHigh = findings.some((f) => f.severity === 'high');
|
|
256
|
-
const status = hasCritical ? 'fail' : hasHigh ? 'warn' : findings.length > 0 ? 'warn' : 'pass';
|
|
257
|
-
const label = findings.length === 0
|
|
258
|
-
? 'Prompt Safety: No injection patterns detected'
|
|
259
|
-
: `Prompt Safety: ${findings.length} suspicious pattern(s) detected`;
|
|
260
|
-
return { status, label, findings };
|
|
11
|
+
export function checkInstructions(instructions, sourceType = 'skill') {
|
|
12
|
+
const result = coreCheckInstructions(instructions, sourceType);
|
|
13
|
+
return {
|
|
14
|
+
status: result.status,
|
|
15
|
+
label: result.label,
|
|
16
|
+
findings: result.findings,
|
|
17
|
+
};
|
|
261
18
|
}
|
|
262
19
|
//# sourceMappingURL=instruction-check.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instruction-check.js","sourceRoot":"","sources":["../../src/checks/instruction-check.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"instruction-check.js","sourceRoot":"","sources":["../../src/checks/instruction-check.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,IAAI,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAGpF;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,YAAoB,EACpB,aAAwC,OAAO;IAE/C,MAAM,MAAM,GAAG,qBAAqB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC/D,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-signals.d.ts","sourceRoot":"","sources":["../src/context-signals.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-signals.js","sourceRoot":"","sources":["../src/context-signals.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAA6B,MAAM,YAAY,CAAC;AAEvF,YAAY,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8E/F"}
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { checkWithAI } from './checks/ai-check.js';
|
|
|
18
18
|
import { checkWithATR } from './checks/atr-check.js';
|
|
19
19
|
import { autoDetectSkillLLM } from './checks/llm-auto-detect.js';
|
|
20
20
|
import { calculateRiskScore } from './risk-scorer.js';
|
|
21
|
+
import { detectContextSignals } from './context-signals.js';
|
|
21
22
|
export { parseSkillManifest } from './manifest-parser.js';
|
|
22
23
|
/**
|
|
23
24
|
* Audit a skill directory for security issues.
|
|
@@ -74,8 +75,12 @@ export async function auditSkill(skillDir, options) {
|
|
|
74
75
|
}
|
|
75
76
|
// 3. Aggregate findings
|
|
76
77
|
const allFindings = checks.flatMap((c) => c.findings);
|
|
77
|
-
// 4.
|
|
78
|
-
const
|
|
78
|
+
// 4. Detect context signals (malicious boosters + legitimate reducers)
|
|
79
|
+
const contextSignals = manifest
|
|
80
|
+
? detectContextSignals(manifest.instructions, manifest)
|
|
81
|
+
: { signals: [], multiplier: 1.0 };
|
|
82
|
+
// 5. Calculate risk score with context multiplier
|
|
83
|
+
const { score, level } = calculateRiskScore(allFindings, contextSignals.multiplier);
|
|
79
84
|
return {
|
|
80
85
|
skillPath: skillDir,
|
|
81
86
|
manifest,
|
|
@@ -83,6 +88,7 @@ export async function auditSkill(skillDir, options) {
|
|
|
83
88
|
riskLevel: level,
|
|
84
89
|
checks,
|
|
85
90
|
findings: allFindings,
|
|
91
|
+
contextSignals,
|
|
86
92
|
auditedAt: new Date().toISOString(),
|
|
87
93
|
durationMs: Date.now() - startTime,
|
|
88
94
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SKILL.md manifest parser
|
|
3
|
-
* SKILL.md 清單解析器
|
|
2
|
+
* SKILL.md manifest parser (filesystem wrapper)
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
* 解析遵循 AgentSkills 規範的 SKILL.md 檔案中的 YAML frontmatter。
|
|
4
|
+
* Reads SKILL.md from disk and delegates to scan-core's string-based parser.
|
|
7
5
|
*
|
|
8
6
|
* @module @panguard-ai/panguard-skill-auditor/manifest-parser
|
|
9
7
|
*/
|
|
10
8
|
import type { SkillManifest } from './types.js';
|
|
11
9
|
/**
|
|
12
10
|
* Parse a SKILL.md file and extract manifest + instructions.
|
|
13
|
-
* 解析 SKILL.md 檔案並擷取清單和指令。
|
|
14
11
|
*/
|
|
15
12
|
export declare function parseSkillManifest(skillDir: string): Promise<SkillManifest | null>;
|
|
16
13
|
//# sourceMappingURL=manifest-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-parser.d.ts","sourceRoot":"","sources":["../src/manifest-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"manifest-parser.d.ts","sourceRoot":"","sources":["../src/manifest-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAexF"}
|
package/dist/manifest-parser.js
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SKILL.md manifest parser
|
|
3
|
-
* SKILL.md 清單解析器
|
|
2
|
+
* SKILL.md manifest parser (filesystem wrapper)
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
* 解析遵循 AgentSkills 規範的 SKILL.md 檔案中的 YAML frontmatter。
|
|
4
|
+
* Reads SKILL.md from disk and delegates to scan-core's string-based parser.
|
|
7
5
|
*
|
|
8
6
|
* @module @panguard-ai/panguard-skill-auditor/manifest-parser
|
|
9
7
|
*/
|
|
10
8
|
import { promises as fs } from 'node:fs';
|
|
11
9
|
import path from 'node:path';
|
|
12
|
-
import
|
|
13
|
-
const
|
|
10
|
+
import { parseManifestFromString } from '@panguard-ai/scan-core';
|
|
11
|
+
const MAX_SKILL_SIZE = 1024 * 1024; // 1 MB
|
|
14
12
|
/**
|
|
15
13
|
* Parse a SKILL.md file and extract manifest + instructions.
|
|
16
|
-
* 解析 SKILL.md 檔案並擷取清單和指令。
|
|
17
14
|
*/
|
|
18
15
|
export async function parseSkillManifest(skillDir) {
|
|
19
16
|
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
20
|
-
const MAX_SKILL_SIZE = 1024 * 1024; // 1 MB
|
|
21
17
|
let content;
|
|
22
18
|
try {
|
|
23
19
|
const stat = await fs.stat(skillPath);
|
|
@@ -29,53 +25,6 @@ export async function parseSkillManifest(skillDir) {
|
|
|
29
25
|
catch {
|
|
30
26
|
return null;
|
|
31
27
|
}
|
|
32
|
-
|
|
33
|
-
if (!match) {
|
|
34
|
-
// No frontmatter — treat entire content as instructions
|
|
35
|
-
return {
|
|
36
|
-
name: path.basename(skillDir),
|
|
37
|
-
description: '',
|
|
38
|
-
instructions: content,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const [, frontmatterRaw, instructions] = match;
|
|
42
|
-
let parsed;
|
|
43
|
-
try {
|
|
44
|
-
parsed =
|
|
45
|
-
yaml.load(frontmatterRaw ?? '', { schema: yaml.JSON_SCHEMA }) ??
|
|
46
|
-
{};
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return {
|
|
50
|
-
name: path.basename(skillDir),
|
|
51
|
-
description: '',
|
|
52
|
-
instructions: instructions ?? content,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
// Parse metadata — can be a JSON string or an object
|
|
56
|
-
let metadata;
|
|
57
|
-
if (typeof parsed['metadata'] === 'string') {
|
|
58
|
-
try {
|
|
59
|
-
metadata = JSON.parse(parsed['metadata']);
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
metadata = undefined;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
else if (typeof parsed['metadata'] === 'object' && parsed['metadata'] !== null) {
|
|
66
|
-
metadata = parsed['metadata'];
|
|
67
|
-
}
|
|
68
|
-
return {
|
|
69
|
-
name: parsed['name'] ?? path.basename(skillDir),
|
|
70
|
-
description: parsed['description'] ?? '',
|
|
71
|
-
license: parsed['license'],
|
|
72
|
-
homepage: parsed['homepage'],
|
|
73
|
-
userInvocable: parsed['user-invocable'],
|
|
74
|
-
disableModelInvocation: parsed['disable-model-invocation'],
|
|
75
|
-
commandDispatch: parsed['command-dispatch'],
|
|
76
|
-
commandTool: parsed['command-tool'],
|
|
77
|
-
metadata,
|
|
78
|
-
instructions: instructions ?? '',
|
|
79
|
-
};
|
|
28
|
+
return parseManifestFromString(content, path.basename(skillDir));
|
|
80
29
|
}
|
|
81
30
|
//# sourceMappingURL=manifest-parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-parser.js","sourceRoot":"","sources":["../src/manifest-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"manifest-parser.js","sourceRoot":"","sources":["../src/manifest-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAE3C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IACvD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAElD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
package/dist/risk-scorer.d.ts
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Risk scoring engine
|
|
3
|
-
*
|
|
4
|
-
* Calculates a 0-100 risk score from audit findings.
|
|
5
|
-
* Deduplicates findings by ID — only the highest severity instance counts.
|
|
2
|
+
* Risk scoring engine - delegates to @panguard-ai/scan-core
|
|
6
3
|
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Calculate risk score (0-100) from findings.
|
|
10
|
-
* Deduplicates by finding ID — keeps the highest severity instance.
|
|
11
|
-
*/
|
|
12
|
-
export declare function calculateRiskScore(findings: AuditFinding[]): {
|
|
13
|
-
score: number;
|
|
14
|
-
level: AuditReport['riskLevel'];
|
|
15
|
-
};
|
|
4
|
+
export { calculateRiskScore } from '@panguard-ai/scan-core';
|
|
16
5
|
//# sourceMappingURL=risk-scorer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"risk-scorer.d.ts","sourceRoot":"","sources":["../src/risk-scorer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"risk-scorer.d.ts","sourceRoot":"","sources":["../src/risk-scorer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/risk-scorer.js
CHANGED
|
@@ -1,52 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Risk scoring engine
|
|
3
|
-
*
|
|
4
|
-
* Calculates a 0-100 risk score from audit findings.
|
|
5
|
-
* Deduplicates findings by ID — only the highest severity instance counts.
|
|
2
|
+
* Risk scoring engine - delegates to @panguard-ai/scan-core
|
|
6
3
|
*/
|
|
7
|
-
|
|
8
|
-
critical: 25,
|
|
9
|
-
high: 15,
|
|
10
|
-
medium: 5,
|
|
11
|
-
low: 1,
|
|
12
|
-
};
|
|
13
|
-
const SEVERITY_RANK = {
|
|
14
|
-
critical: 4,
|
|
15
|
-
high: 3,
|
|
16
|
-
medium: 2,
|
|
17
|
-
low: 1,
|
|
18
|
-
info: 0,
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Calculate risk score (0-100) from findings.
|
|
22
|
-
* Deduplicates by finding ID — keeps the highest severity instance.
|
|
23
|
-
*/
|
|
24
|
-
export function calculateRiskScore(findings) {
|
|
25
|
-
// Deduplicate: keep highest severity per finding ID
|
|
26
|
-
const deduped = new Map();
|
|
27
|
-
for (const finding of findings) {
|
|
28
|
-
const existing = deduped.get(finding.id);
|
|
29
|
-
if (!existing ||
|
|
30
|
-
(SEVERITY_RANK[finding.severity] ?? 0) > (SEVERITY_RANK[existing.severity] ?? 0)) {
|
|
31
|
-
deduped.set(finding.id, finding);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
let rawScore = 0;
|
|
35
|
-
for (const finding of deduped.values()) {
|
|
36
|
-
rawScore += SEVERITY_WEIGHTS[finding.severity] ?? 0;
|
|
37
|
-
}
|
|
38
|
-
const score = Math.min(100, rawScore);
|
|
39
|
-
// Critical-override: any critical finding forces at least HIGH risk level
|
|
40
|
-
const hasCritical = [...deduped.values()].some((f) => f.severity === 'critical');
|
|
41
|
-
let level;
|
|
42
|
-
if (score >= 70 || (hasCritical && score >= 25))
|
|
43
|
-
level = 'CRITICAL';
|
|
44
|
-
else if (score >= 40 || hasCritical)
|
|
45
|
-
level = 'HIGH';
|
|
46
|
-
else if (score >= 15)
|
|
47
|
-
level = 'MEDIUM';
|
|
48
|
-
else
|
|
49
|
-
level = 'LOW';
|
|
50
|
-
return { score, level };
|
|
51
|
-
}
|
|
4
|
+
export { calculateRiskScore } from '@panguard-ai/scan-core';
|
|
52
5
|
//# sourceMappingURL=risk-scorer.js.map
|
package/dist/risk-scorer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"risk-scorer.js","sourceRoot":"","sources":["../src/risk-scorer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"risk-scorer.js","sourceRoot":"","sources":["../src/risk-scorer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,51 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Panguard Skill Auditor - Type definitions
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Re-exports core scan types from @panguard-ai/scan-core
|
|
5
|
+
* and adds CLI-specific types (AuditReport, AuditOptions).
|
|
4
6
|
*
|
|
5
7
|
* @module @panguard-ai/panguard-skill-auditor/types
|
|
6
8
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
description: string;
|
|
12
|
-
license?: string;
|
|
13
|
-
homepage?: string;
|
|
14
|
-
userInvocable?: boolean;
|
|
15
|
-
disableModelInvocation?: boolean;
|
|
16
|
-
commandDispatch?: string;
|
|
17
|
-
commandTool?: string;
|
|
18
|
-
metadata?: SkillMetadata;
|
|
19
|
-
/** Raw instruction body (after frontmatter) */
|
|
20
|
-
instructions: string;
|
|
21
|
-
}
|
|
22
|
-
export interface SkillMetadata {
|
|
23
|
-
author?: string;
|
|
24
|
-
version?: string;
|
|
25
|
-
tags?: string[];
|
|
26
|
-
triggers?: string[];
|
|
27
|
-
openclaw?: {
|
|
28
|
-
requires?: {
|
|
29
|
-
bins?: string[];
|
|
30
|
-
env?: string[];
|
|
31
|
-
config?: string[];
|
|
32
|
-
};
|
|
33
|
-
primaryEnv?: string;
|
|
34
|
-
os?: string[];
|
|
35
|
-
always?: boolean;
|
|
36
|
-
homepage?: string;
|
|
37
|
-
};
|
|
38
|
-
[key: string]: unknown;
|
|
39
|
-
}
|
|
40
|
-
/** Single audit finding */
|
|
41
|
-
export interface AuditFinding {
|
|
42
|
-
id: string;
|
|
43
|
-
title: string;
|
|
44
|
-
description: string;
|
|
45
|
-
severity: Severity;
|
|
46
|
-
category: 'manifest' | 'prompt-injection' | 'tool-poisoning' | 'context-exfiltration' | 'agent-manipulation' | 'privilege-escalation' | 'excessive-autonomy' | 'data-poisoning' | 'model-abuse' | 'skill-compromise' | 'code' | 'secrets' | 'dependency' | 'permission' | 'ai-analysis' | 'atr';
|
|
47
|
-
location?: string;
|
|
48
|
-
}
|
|
9
|
+
export type { Severity, FindingCategory, SkillManifest, SkillMetadata, RiskLevel, } from '@panguard-ai/scan-core';
|
|
10
|
+
import type { Finding, RiskLevel } from '@panguard-ai/scan-core';
|
|
11
|
+
/** Single audit finding (alias for scan-core Finding) */
|
|
12
|
+
export type AuditFinding = Finding;
|
|
49
13
|
/** Result of a single check category */
|
|
50
14
|
export interface CheckResult {
|
|
51
15
|
status: 'pass' | 'warn' | 'fail' | 'info';
|
|
@@ -56,18 +20,14 @@ export interface CheckResult {
|
|
|
56
20
|
export interface AuditOptions {
|
|
57
21
|
/**
|
|
58
22
|
* LLM provider for AI semantic analysis (Layer 2).
|
|
59
|
-
* When omitted, the auditor auto-detects an available provider
|
|
60
|
-
* 1. ANTHROPIC_API_KEY env var (Claude)
|
|
61
|
-
* 2. OPENAI_API_KEY env var (OpenAI)
|
|
62
|
-
* 3. Local Ollama instance
|
|
63
|
-
* If no provider is found, an info-level finding is added to the report.
|
|
23
|
+
* When omitted, the auditor auto-detects an available provider.
|
|
64
24
|
*/
|
|
65
25
|
llm?: import('./checks/ai-check.js').SkillAnalysisLLM;
|
|
66
|
-
/** Skip AI analysis entirely
|
|
26
|
+
/** Skip AI analysis entirely */
|
|
67
27
|
skipAI?: boolean;
|
|
68
28
|
/** Skip ATR pattern detection */
|
|
69
29
|
skipATR?: boolean;
|
|
70
|
-
/** Additional ATR rules fetched from Threat Cloud
|
|
30
|
+
/** Additional ATR rules fetched from Threat Cloud */
|
|
71
31
|
cloudRules?: Array<{
|
|
72
32
|
id: string;
|
|
73
33
|
title: string;
|
|
@@ -78,11 +38,21 @@ export interface AuditOptions {
|
|
|
78
38
|
/** Complete audit report */
|
|
79
39
|
export interface AuditReport {
|
|
80
40
|
skillPath: string;
|
|
81
|
-
manifest: SkillManifest | null;
|
|
41
|
+
manifest: import('@panguard-ai/scan-core').SkillManifest | null;
|
|
82
42
|
riskScore: number;
|
|
83
|
-
riskLevel:
|
|
43
|
+
riskLevel: RiskLevel;
|
|
84
44
|
checks: CheckResult[];
|
|
85
45
|
findings: AuditFinding[];
|
|
46
|
+
/** Context signals that adjusted the risk score */
|
|
47
|
+
contextSignals?: {
|
|
48
|
+
signals: ReadonlyArray<{
|
|
49
|
+
id: string;
|
|
50
|
+
type: 'booster' | 'reducer';
|
|
51
|
+
label: string;
|
|
52
|
+
weight: number;
|
|
53
|
+
}>;
|
|
54
|
+
multiplier: number;
|
|
55
|
+
};
|
|
86
56
|
auditedAt: string;
|
|
87
57
|
durationMs: number;
|
|
88
58
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,YAAY,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EACb,aAAa,EACb,SAAS,GACV,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EACV,OAAO,EAGP,SAAS,EACV,MAAM,wBAAwB,CAAC;AAMhC,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC;AAEnC,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,6BAA6B;AAC7B,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,sBAAsB,EAAE,gBAAgB,CAAC;IACtD,gCAAgC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/F;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,wBAAwB,EAAE,aAAa,GAAG,IAAI,CAAC;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,mDAAmD;IACnD,cAAc,CAAC,EAAE;QACf,OAAO,EAAE,aAAa,CAAC;YACrB,EAAE,EAAE,MAAM,CAAC;YACX,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;YAC5B,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC,CAAC;QACH,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
package/dist/types.js
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panguard-ai/panguard-skill-auditor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,23 +28,24 @@
|
|
|
28
28
|
"package.json",
|
|
29
29
|
"README.md"
|
|
30
30
|
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc --build",
|
|
33
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"dev": "tsc --build --watch"
|
|
37
|
+
},
|
|
31
38
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"@panguard-ai/
|
|
34
|
-
"@panguard-ai/
|
|
35
|
-
"@panguard-ai/
|
|
39
|
+
"@panguard-ai/atr": "workspace:*",
|
|
40
|
+
"@panguard-ai/core": "workspace:*",
|
|
41
|
+
"@panguard-ai/panguard-scan": "workspace:*",
|
|
42
|
+
"@panguard-ai/scan-core": "workspace:*",
|
|
43
|
+
"js-yaml": "^4.1.0"
|
|
36
44
|
},
|
|
37
45
|
"devDependencies": {
|
|
38
46
|
"@types/js-yaml": "^4.0.9",
|
|
39
47
|
"@types/node": "^22.14.0",
|
|
40
48
|
"typescript": "~5.7.3",
|
|
41
49
|
"vitest": "^3.0.0"
|
|
42
|
-
},
|
|
43
|
-
"scripts": {
|
|
44
|
-
"build": "tsc --build",
|
|
45
|
-
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
46
|
-
"typecheck": "tsc --noEmit",
|
|
47
|
-
"test": "vitest run",
|
|
48
|
-
"dev": "tsc --build --watch"
|
|
49
50
|
}
|
|
50
|
-
}
|
|
51
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-2026 Panguard AI Team
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|