hackmyagent 0.16.0 → 0.16.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.
Files changed (47) hide show
  1. package/dist/.integrity-manifest.json +1 -1
  2. package/dist/arp/intelligence/nanomind-l1.d.ts +30 -0
  3. package/dist/arp/intelligence/nanomind-l1.d.ts.map +1 -1
  4. package/dist/arp/intelligence/nanomind-l1.js +115 -0
  5. package/dist/arp/intelligence/nanomind-l1.js.map +1 -1
  6. package/dist/cli.js +220 -19
  7. package/dist/cli.js.map +1 -1
  8. package/dist/hardening/scanner.d.ts.map +1 -1
  9. package/dist/hardening/scanner.js +125 -4
  10. package/dist/hardening/scanner.js.map +1 -1
  11. package/dist/hardening/taxonomy.d.ts +2 -0
  12. package/dist/hardening/taxonomy.d.ts.map +1 -1
  13. package/dist/hardening/taxonomy.js +5 -0
  14. package/dist/hardening/taxonomy.js.map +1 -1
  15. package/dist/nanomind-core/analyzers/stego-analyzer.d.ts +30 -0
  16. package/dist/nanomind-core/analyzers/stego-analyzer.d.ts.map +1 -0
  17. package/dist/nanomind-core/analyzers/stego-analyzer.js +533 -0
  18. package/dist/nanomind-core/analyzers/stego-analyzer.js.map +1 -0
  19. package/dist/nanomind-core/daemon-lifecycle.d.ts +28 -0
  20. package/dist/nanomind-core/daemon-lifecycle.d.ts.map +1 -0
  21. package/dist/nanomind-core/daemon-lifecycle.js +142 -0
  22. package/dist/nanomind-core/daemon-lifecycle.js.map +1 -0
  23. package/dist/nanomind-core/inference/tme-classifier.d.ts +3 -2
  24. package/dist/nanomind-core/inference/tme-classifier.d.ts.map +1 -1
  25. package/dist/nanomind-core/inference/tme-classifier.js +26 -16
  26. package/dist/nanomind-core/inference/tme-classifier.js.map +1 -1
  27. package/dist/nanomind-core/orchestrate.d.ts.map +1 -1
  28. package/dist/nanomind-core/orchestrate.js +11 -1
  29. package/dist/nanomind-core/orchestrate.js.map +1 -1
  30. package/dist/nanomind-core/scanner-bridge.d.ts.map +1 -1
  31. package/dist/nanomind-core/scanner-bridge.js +6 -0
  32. package/dist/nanomind-core/scanner-bridge.js.map +1 -1
  33. package/dist/plugins/credvault.d.ts.map +1 -1
  34. package/dist/plugins/credvault.js +25 -0
  35. package/dist/plugins/credvault.js.map +1 -1
  36. package/dist/semantic/nanomind-enhancer.d.ts.map +1 -1
  37. package/dist/semantic/nanomind-enhancer.js +206 -0
  38. package/dist/semantic/nanomind-enhancer.js.map +1 -1
  39. package/dist/telemetry/nanomind-feedback.d.ts +43 -0
  40. package/dist/telemetry/nanomind-feedback.d.ts.map +1 -0
  41. package/dist/telemetry/nanomind-feedback.js +104 -0
  42. package/dist/telemetry/nanomind-feedback.js.map +1 -0
  43. package/dist/telemetry/nanomind-telemetry.d.ts +48 -0
  44. package/dist/telemetry/nanomind-telemetry.d.ts.map +1 -0
  45. package/dist/telemetry/nanomind-telemetry.js +123 -0
  46. package/dist/telemetry/nanomind-telemetry.js.map +1 -0
  47. package/package.json +1 -1
@@ -9,6 +9,8 @@ import type { SecurityFinding } from './security-check';
9
9
  * Returns undefined if no mapping exists.
10
10
  */
11
11
  export declare function getAttackClass(checkId: string): string | undefined;
12
+ /** Return a copy of the full taxonomy map (checkId -> attackClass). */
13
+ export declare function getTaxonomyMap(): Record<string, string>;
12
14
  /**
13
15
  * Enrich an array of SecurityFindings with their attack class mappings.
14
16
  * Modifies findings in place.
@@ -1 +1 @@
1
- {"version":3,"file":"taxonomy.d.ts","sourceRoot":"","sources":["../../src/hardening/taxonomy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAuQxD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI,CAOpE"}
1
+ {"version":3,"file":"taxonomy.d.ts","sourceRoot":"","sources":["../../src/hardening/taxonomy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAuQxD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAED,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEvD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI,CAOpE"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.getAttackClass = getAttackClass;
9
+ exports.getTaxonomyMap = getTaxonomyMap;
9
10
  exports.enrichWithTaxonomy = enrichWithTaxonomy;
10
11
  /** Maps HMA check ID prefixes and exact IDs to attack class identifiers */
11
12
  const TAXONOMY_MAP = {
@@ -252,6 +253,10 @@ const TAXONOMY_MAP = {
252
253
  function getAttackClass(checkId) {
253
254
  return TAXONOMY_MAP[checkId];
254
255
  }
256
+ /** Return a copy of the full taxonomy map (checkId -> attackClass). */
257
+ function getTaxonomyMap() {
258
+ return { ...TAXONOMY_MAP };
259
+ }
255
260
  /**
256
261
  * Enrich an array of SecurityFindings with their attack class mappings.
257
262
  * Modifies findings in place.
@@ -1 +1 @@
1
- {"version":3,"file":"taxonomy.js","sourceRoot":"","sources":["../../src/hardening/taxonomy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"taxonomy.js","sourceRoot":"","sources":["../../src/hardening/taxonomy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Steganography Analyzer -- AST-based UNICODE-STEGO-* checks
3
+ *
4
+ * Provides semantic context that the static byte-level scanner cannot:
5
+ * - Distinguishes emoji variation selectors from data-encoding variation selectors
6
+ * - Distinguishes Cyrillic in Cyrillic text (i18n) from Cyrillic in Latin words (homoglyphs)
7
+ * - Distinguishes ZWNJ/ZWJ in Indic/Arabic text from hidden payload encoding
8
+ * - Uses TME classifier for file-level intent as a secondary signal
9
+ *
10
+ * Defense-in-depth: this analyzer can DOWNGRADE stego findings from the static
11
+ * scanner but cannot SUPPRESS them entirely. A downgraded finding still appears
12
+ * in results with evidence explaining why it's benign.
13
+ *
14
+ * Addresses false positives like @upstash/context7-mcp where emoji headers
15
+ * and i18n translation badges triggered CRITICAL steganography findings.
16
+ */
17
+ import type { SecurityAST } from '../types.js';
18
+ import type { ASTFinding } from './capability-analyzer.js';
19
+ /**
20
+ * Analyze a SecurityAST for steganography patterns.
21
+ * Produces findings compatible with the static UNICODE-STEGO-* checks,
22
+ * but with semantic context that enables false-positive reduction.
23
+ *
24
+ * Each finding includes:
25
+ * - passed: true if the Unicode is benign (legitimate emoji, i18n, etc.)
26
+ * - confidence: how sure we are about the verdict
27
+ * - evidence: human-readable explanation
28
+ */
29
+ export declare function analyzeSteganography(ast: SecurityAST): ASTFinding[];
30
+ //# sourceMappingURL=stego-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stego-analyzer.d.ts","sourceRoot":"","sources":["../../../src/nanomind-core/analyzers/stego-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AA0S3D;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,WAAW,GACf,UAAU,EAAE,CA6Qd"}
@@ -0,0 +1,533 @@
1
+ "use strict";
2
+ /**
3
+ * Steganography Analyzer -- AST-based UNICODE-STEGO-* checks
4
+ *
5
+ * Provides semantic context that the static byte-level scanner cannot:
6
+ * - Distinguishes emoji variation selectors from data-encoding variation selectors
7
+ * - Distinguishes Cyrillic in Cyrillic text (i18n) from Cyrillic in Latin words (homoglyphs)
8
+ * - Distinguishes ZWNJ/ZWJ in Indic/Arabic text from hidden payload encoding
9
+ * - Uses TME classifier for file-level intent as a secondary signal
10
+ *
11
+ * Defense-in-depth: this analyzer can DOWNGRADE stego findings from the static
12
+ * scanner but cannot SUPPRESS them entirely. A downgraded finding still appears
13
+ * in results with evidence explaining why it's benign.
14
+ *
15
+ * Addresses false positives like @upstash/context7-mcp where emoji headers
16
+ * and i18n translation badges triggered CRITICAL steganography findings.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.analyzeSteganography = analyzeSteganography;
20
+ // ============================================================================
21
+ // Unicode Constants
22
+ // ============================================================================
23
+ // Steganographic characters (invisible, format-control)
24
+ const STEGO_RANGES = [
25
+ [0x200B, 0x200D], // Zero-width space, ZWNJ, ZWJ
26
+ [0x2060, 0x2060], // Word joiner
27
+ [0x00AD, 0x00AD], // Soft hyphen
28
+ [0x202A, 0x202E], // Bidi overrides (LRE, RLE, PDF, LRO, RLO)
29
+ [0x2066, 0x2069], // Bidi isolates (LRI, RLI, FSI, PDI)
30
+ [0xFE00, 0xFE0F], // Variation selectors
31
+ [0xE0000, 0xE01EF], // Tag characters
32
+ ];
33
+ // Cyrillic characters commonly used as Latin homoglyphs
34
+ const CYRILLIC_HOMOGLYPHS = new Set([
35
+ 0x0430, // а (a)
36
+ 0x0441, // с (c)
37
+ 0x0435, // е (e)
38
+ 0x043E, // о (o)
39
+ 0x0440, // р (p)
40
+ 0x0445, // х (x)
41
+ 0x0443, // у (y)
42
+ 0x041D, // Н (H)
43
+ 0x0412, // В (B)
44
+ 0x0422, // Т (T)
45
+ 0x041C, // М (M)
46
+ 0x041A, // К (K)
47
+ ]);
48
+ // Emoji base characters that legitimately precede variation selectors
49
+ // Includes keycap bases, symbols, and common emoji
50
+ const EMOJI_BASES = new Set([
51
+ 0x0023, // # (keycap)
52
+ 0x002A, // * (keycap)
53
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, // 0-4 (keycaps)
54
+ 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, // 5-9 (keycaps)
55
+ 0x2600, 0x2601, 0x2602, 0x2603, // weather symbols
56
+ 0x2614, 0x2615, // umbrella, hot beverage
57
+ 0x2622, 0x2623, // radioactive, biohazard
58
+ 0x2626, 0x262A, 0x262E, 0x262F, // religious symbols
59
+ 0x2639, 0x263A, // frowning, smiling face
60
+ 0x2640, 0x2642, // gender symbols
61
+ 0x2660, 0x2663, 0x2665, 0x2666, // card suits
62
+ 0x2692, 0x2693, 0x2694, 0x2695, 0x2696, 0x2697, // tools
63
+ 0x2699, // gear
64
+ 0x269B, 0x269C, // atom, fleur-de-lis
65
+ 0x26A0, 0x26A1, // warning, high voltage
66
+ 0x26AA, 0x26AB, // circles
67
+ 0x26B0, 0x26B1, // coffin, urn
68
+ 0x26BD, 0x26BE, // sports
69
+ 0x26C4, 0x26C5, // weather
70
+ 0x26CE, 0x26CF, // Ophiuchus, pick
71
+ 0x26D1, // helmet
72
+ 0x26D3, 0x26D4, // chains, no entry
73
+ 0x26E9, 0x26EA, // shinto, church
74
+ 0x26F0, 0x26F1, 0x26F2, 0x26F3, 0x26F4, 0x26F5, // misc
75
+ 0x26F7, 0x26F8, 0x26F9, 0x26FA, // sports/tent
76
+ 0x26FD, // fuel pump
77
+ 0x2702, // scissors
78
+ 0x2708, 0x2709, // airplane, envelope
79
+ 0x270A, 0x270B, 0x270C, 0x270D, // hands
80
+ 0x270F, // pencil
81
+ 0x2712, // pen
82
+ 0x2714, // check mark
83
+ 0x2716, // x mark
84
+ 0x271D, // cross
85
+ 0x2721, // star of david
86
+ 0x2728, // sparkles
87
+ 0x2733, 0x2734, // asterisks
88
+ 0x2744, // snowflake
89
+ 0x2747, // sparkle
90
+ 0x274C, 0x274E, // cross marks
91
+ 0x2753, 0x2754, 0x2755, // question/exclamation
92
+ 0x2757, // exclamation
93
+ 0x2763, 0x2764, // heart exclamation, heart
94
+ 0x2795, 0x2796, 0x2797, // math symbols
95
+ 0x27A1, // right arrow
96
+ 0x27B0, 0x27BF, // curly loop
97
+ ]);
98
+ // CJK Unicode ranges
99
+ const CJK_RANGES = [
100
+ [0x4E00, 0x9FFF], // CJK Unified Ideographs
101
+ [0x3400, 0x4DBF], // CJK Extension A
102
+ [0x3000, 0x303F], // CJK Symbols and Punctuation
103
+ [0x3040, 0x309F], // Hiragana
104
+ [0x30A0, 0x30FF], // Katakana
105
+ [0xAC00, 0xD7AF], // Hangul Syllables
106
+ [0xFF00, 0xFFEF], // Fullwidth Forms
107
+ ];
108
+ // Cyrillic Unicode ranges
109
+ const CYRILLIC_RANGES = [
110
+ [0x0400, 0x04FF], // Cyrillic
111
+ [0x0500, 0x052F], // Cyrillic Supplement
112
+ ];
113
+ // Arabic Unicode ranges
114
+ const ARABIC_RANGES = [
115
+ [0x0600, 0x06FF], // Arabic
116
+ [0x0750, 0x077F], // Arabic Supplement
117
+ [0xFB50, 0xFDFF], // Arabic Presentation Forms-A
118
+ [0xFE70, 0xFEFF], // Arabic Presentation Forms-B
119
+ ];
120
+ // Devanagari/Indic ranges (where ZWNJ/ZWJ are grammatically required)
121
+ const INDIC_RANGES = [
122
+ [0x0900, 0x097F], // Devanagari
123
+ [0x0980, 0x09FF], // Bengali
124
+ [0x0A00, 0x0A7F], // Gurmukhi
125
+ [0x0A80, 0x0AFF], // Gujarati
126
+ [0x0B00, 0x0B7F], // Oriya
127
+ [0x0B80, 0x0BFF], // Tamil
128
+ [0x0C00, 0x0C7F], // Telugu
129
+ [0x0C80, 0x0CFF], // Kannada
130
+ [0x0D00, 0x0D7F], // Malayalam
131
+ ];
132
+ // ============================================================================
133
+ // Helper Functions
134
+ // ============================================================================
135
+ function inRanges(cp, ranges) {
136
+ return ranges.some(([lo, hi]) => cp >= lo && cp <= hi);
137
+ }
138
+ function isStegoChar(cp) {
139
+ return inRanges(cp, STEGO_RANGES);
140
+ }
141
+ function isCJK(cp) {
142
+ return inRanges(cp, CJK_RANGES);
143
+ }
144
+ function isCyrillic(cp) {
145
+ return inRanges(cp, CYRILLIC_RANGES);
146
+ }
147
+ function isArabic(cp) {
148
+ return inRanges(cp, ARABIC_RANGES);
149
+ }
150
+ function isIndic(cp) {
151
+ return inRanges(cp, INDIC_RANGES);
152
+ }
153
+ function isLatinAlpha(cp) {
154
+ return (cp >= 0x41 && cp <= 0x5A) || (cp >= 0x61 && cp <= 0x7A);
155
+ }
156
+ /**
157
+ * Check if a high-codepoint character (>= 0x10000) is a standard emoji.
158
+ * Most emoji are in the Supplementary Multilingual Plane.
159
+ */
160
+ function isEmoji(cp) {
161
+ return ((cp >= 0x1F300 && cp <= 0x1F9FF) || // Misc Symbols, Emoticons, etc.
162
+ (cp >= 0x1FA00 && cp <= 0x1FAFF) || // Symbols Extended-A
163
+ (cp >= 0x2600 && cp <= 0x27BF) || // Misc symbols (also in BMP)
164
+ EMOJI_BASES.has(cp));
165
+ }
166
+ function extractContexts(text) {
167
+ const contexts = [];
168
+ const codepoints = [...text].map(c => c.codePointAt(0));
169
+ for (let i = 0; i < codepoints.length; i++) {
170
+ const cp = codepoints[i];
171
+ if (!isStegoChar(cp) && !CYRILLIC_HOMOGLYPHS.has(cp))
172
+ continue;
173
+ const line = text.slice(0, [...text].slice(0, i + 1).join('').length).split('\n').length;
174
+ const before = codepoints.slice(Math.max(0, i - 10), i);
175
+ const after = codepoints.slice(i + 1, Math.min(codepoints.length, i + 11));
176
+ contexts.push({ codepoint: cp, position: i, line, before, after });
177
+ }
178
+ return contexts;
179
+ }
180
+ // ============================================================================
181
+ // Benign Pattern Detectors
182
+ // ============================================================================
183
+ /**
184
+ * Check if a variation selector (U+FE0F) is a legitimate emoji presentation selector.
185
+ * Returns true if it follows an emoji base character or is part of a keycap sequence.
186
+ */
187
+ function isLegitimateVariationSelector(ctx) {
188
+ if (ctx.codepoint < 0xFE00 || ctx.codepoint > 0xFE0F)
189
+ return false;
190
+ // Check if preceded by an emoji base or emoji character
191
+ if (ctx.before.length > 0) {
192
+ const prev = ctx.before[ctx.before.length - 1];
193
+ if (EMOJI_BASES.has(prev) || isEmoji(prev))
194
+ return true;
195
+ // Keycap sequence: digit/# + FE0F + 20E3
196
+ if ((prev >= 0x30 && prev <= 0x39) || prev === 0x23 || prev === 0x2A)
197
+ return true;
198
+ }
199
+ // Check if followed by combining enclosing keycap (U+20E3)
200
+ if (ctx.after.length > 0 && ctx.after[0] === 0x20E3)
201
+ return true;
202
+ return false;
203
+ }
204
+ /**
205
+ * Check if a ZWJ (U+200D) is a legitimate emoji ZWJ sequence or Indic conjunct.
206
+ * Returns true for family emoji, profession emoji, flag sequences, or Indic text.
207
+ */
208
+ function isLegitimateZWJ(ctx) {
209
+ if (ctx.codepoint !== 0x200D)
210
+ return false;
211
+ // Check for emoji context (emoji before and after ZWJ)
212
+ const prevEmoji = ctx.before.length > 0 && (isEmoji(ctx.before[ctx.before.length - 1]) || ctx.before[ctx.before.length - 1] === 0xFE0F);
213
+ const nextEmoji = ctx.after.length > 0 && isEmoji(ctx.after[0]);
214
+ if (prevEmoji && nextEmoji)
215
+ return true;
216
+ // Check for Indic conjunct context
217
+ const hasIndic = ctx.before.some(c => isIndic(c)) || ctx.after.some(c => isIndic(c));
218
+ if (hasIndic)
219
+ return true;
220
+ return false;
221
+ }
222
+ /**
223
+ * Check if a ZWNJ (U+200C) is a legitimate Persian/Arabic text separator.
224
+ * In Persian, ZWNJ is grammatically required between prefix and word.
225
+ */
226
+ function isLegitimateZWNJ(ctx) {
227
+ if (ctx.codepoint !== 0x200C)
228
+ return false;
229
+ // Arabic/Persian context: ZWNJ between Arabic chars
230
+ const hasArabic = ctx.before.some(c => isArabic(c)) || ctx.after.some(c => isArabic(c));
231
+ if (hasArabic)
232
+ return true;
233
+ // Indic context: ZWNJ for half-forms
234
+ const hasIndic = ctx.before.some(c => isIndic(c)) || ctx.after.some(c => isIndic(c));
235
+ if (hasIndic)
236
+ return true;
237
+ return false;
238
+ }
239
+ /**
240
+ * Check if a Cyrillic character is a homoglyph attack (Cyrillic mixed into Latin)
241
+ * vs legitimate Cyrillic text (i18n badges, translations, etc.).
242
+ *
243
+ * Attack pattern: "cоnfig" (Cyrillic о in a Latin word)
244
+ * Benign pattern: "Документация" (pure Cyrillic text)
245
+ */
246
+ function isCyrillicHomoglyphAttack(ctx) {
247
+ if (!CYRILLIC_HOMOGLYPHS.has(ctx.codepoint))
248
+ return false;
249
+ // Check if surrounded by Latin characters (= homoglyph attack)
250
+ const prevLatin = ctx.before.length > 0 && isLatinAlpha(ctx.before[ctx.before.length - 1]);
251
+ const nextLatin = ctx.after.length > 0 && isLatinAlpha(ctx.after[0]);
252
+ // If both neighbors are Latin, this is almost certainly a homoglyph attack
253
+ if (prevLatin && nextLatin)
254
+ return true;
255
+ // If one neighbor is Latin and the other is space/punctuation, still suspicious
256
+ if (prevLatin || nextLatin) {
257
+ // But check if there's more Cyrillic nearby (might be a Cyrillic word adjacent to Latin)
258
+ const nearbyCyrillic = [...ctx.before, ...ctx.after].filter(c => isCyrillic(c)).length;
259
+ // If very few Cyrillic chars among mostly Latin = attack
260
+ return nearbyCyrillic < 3;
261
+ }
262
+ // Surrounded by Cyrillic or non-Latin = legitimate i18n text
263
+ return false;
264
+ }
265
+ // ============================================================================
266
+ // Main Analyzer
267
+ // ============================================================================
268
+ /**
269
+ * Analyze a SecurityAST for steganography patterns.
270
+ * Produces findings compatible with the static UNICODE-STEGO-* checks,
271
+ * but with semantic context that enables false-positive reduction.
272
+ *
273
+ * Each finding includes:
274
+ * - passed: true if the Unicode is benign (legitimate emoji, i18n, etc.)
275
+ * - confidence: how sure we are about the verdict
276
+ * - evidence: human-readable explanation
277
+ */
278
+ function analyzeSteganography(ast) {
279
+ const findings = [];
280
+ // Get the raw text from evidence spans or declared purpose
281
+ const text = ast.evidenceSpans.map(s => s.text).join('\n') || ast.declaredPurpose || '';
282
+ if (!text)
283
+ return findings;
284
+ const contexts = extractContexts(text);
285
+ if (contexts.length === 0)
286
+ return findings;
287
+ // Analyze each suspicious character in context
288
+ const variationSelectors = contexts.filter(c => c.codepoint >= 0xFE00 && c.codepoint <= 0xFE0F);
289
+ const zwjChars = contexts.filter(c => c.codepoint === 0x200D);
290
+ const zwnjChars = contexts.filter(c => c.codepoint === 0x200C);
291
+ const homoglyphs = contexts.filter(c => CYRILLIC_HOMOGLYPHS.has(c.codepoint));
292
+ const zwsChars = contexts.filter(c => c.codepoint === 0x200B);
293
+ const bidiChars = contexts.filter(c => (c.codepoint >= 0x202A && c.codepoint <= 0x202E) ||
294
+ (c.codepoint >= 0x2066 && c.codepoint <= 0x2069));
295
+ const tagChars = contexts.filter(c => c.codepoint >= 0xE0000 && c.codepoint <= 0xE01EF);
296
+ const softHyphens = contexts.filter(c => c.codepoint === 0x00AD);
297
+ const wordJoiners = contexts.filter(c => c.codepoint === 0x2060);
298
+ // ── Variation Selectors ──
299
+ if (variationSelectors.length > 0) {
300
+ const legitimate = variationSelectors.filter(isLegitimateVariationSelector);
301
+ const suspicious = variationSelectors.length - legitimate.length;
302
+ if (suspicious === 0) {
303
+ // ALL variation selectors are legitimate emoji presentation selectors
304
+ findings.push({
305
+ checkId: 'UNICODE-STEGO-001',
306
+ name: 'Variation Selectors - Legitimate Emoji',
307
+ description: `Found ${variationSelectors.length} variation selector(s), all in emoji contexts`,
308
+ category: 'unicode-stego',
309
+ severity: 'info',
310
+ passed: true,
311
+ message: `All ${variationSelectors.length} variation selectors are emoji presentation selectors (U+FE0F after emoji base characters)`,
312
+ fixable: false,
313
+ file: ast.artifactPath,
314
+ attackClass: 'UNICODE-STEGO',
315
+ confidence: 0.95,
316
+ evidence: `Emoji patterns: ${legitimate.slice(0, 3).map(c => `U+${c.codepoint.toString(16).toUpperCase()} at L${c.line}`).join(', ')}`,
317
+ });
318
+ }
319
+ else {
320
+ // Some variation selectors are NOT in emoji contexts
321
+ findings.push({
322
+ checkId: 'UNICODE-STEGO-001',
323
+ name: 'Suspicious Variation Selectors',
324
+ description: `Found ${suspicious} variation selector(s) outside emoji contexts`,
325
+ category: 'unicode-stego',
326
+ severity: 'high',
327
+ passed: false,
328
+ message: `${suspicious} of ${variationSelectors.length} variation selectors are not emoji presentation selectors - possible data encoding`,
329
+ fixable: false,
330
+ file: ast.artifactPath,
331
+ attackClass: 'UNICODE-STEGO',
332
+ confidence: 0.85,
333
+ evidence: `Suspicious at lines: ${variationSelectors.filter(c => !isLegitimateVariationSelector(c)).slice(0, 5).map(c => `L${c.line}`).join(', ')}`,
334
+ });
335
+ }
336
+ }
337
+ // ── ZWJ Characters ──
338
+ if (zwjChars.length > 0) {
339
+ const legitimate = zwjChars.filter(isLegitimateZWJ);
340
+ const suspicious = zwjChars.length - legitimate.length;
341
+ if (suspicious === 0) {
342
+ findings.push({
343
+ checkId: 'UNICODE-STEGO-001',
344
+ name: 'Zero-Width Joiners - Legitimate',
345
+ description: `Found ${zwjChars.length} ZWJ(s), all in emoji/Indic contexts`,
346
+ category: 'unicode-stego',
347
+ severity: 'info',
348
+ passed: true,
349
+ message: `All ${zwjChars.length} ZWJ characters are legitimate (emoji sequences or Indic conjuncts)`,
350
+ fixable: false,
351
+ file: ast.artifactPath,
352
+ attackClass: 'UNICODE-STEGO',
353
+ confidence: 0.92,
354
+ evidence: 'ZWJ used in emoji ZWJ sequences or Indic text rendering',
355
+ });
356
+ }
357
+ // If suspicious, let static scanner handle it (defense-in-depth)
358
+ }
359
+ // ── ZWNJ Characters ──
360
+ if (zwnjChars.length > 0) {
361
+ const legitimate = zwnjChars.filter(isLegitimateZWNJ);
362
+ const suspicious = zwnjChars.length - legitimate.length;
363
+ if (suspicious === 0) {
364
+ findings.push({
365
+ checkId: 'UNICODE-STEGO-001',
366
+ name: 'Zero-Width Non-Joiners - Legitimate',
367
+ description: `Found ${zwnjChars.length} ZWNJ(s), all in Arabic/Indic contexts`,
368
+ category: 'unicode-stego',
369
+ severity: 'info',
370
+ passed: true,
371
+ message: `All ${zwnjChars.length} ZWNJ characters are legitimate (Persian/Arabic grammar or Indic half-forms)`,
372
+ fixable: false,
373
+ file: ast.artifactPath,
374
+ attackClass: 'UNICODE-STEGO',
375
+ confidence: 0.90,
376
+ evidence: 'ZWNJ used in Persian/Arabic text separation or Indic rendering',
377
+ });
378
+ }
379
+ }
380
+ // ── Cyrillic Homoglyphs ──
381
+ if (homoglyphs.length > 0) {
382
+ const attacks = homoglyphs.filter(isCyrillicHomoglyphAttack);
383
+ const legitimate = homoglyphs.length - attacks.length;
384
+ if (attacks.length === 0) {
385
+ // All Cyrillic chars are in Cyrillic text blocks (i18n)
386
+ findings.push({
387
+ checkId: 'UNICODE-STEGO-005',
388
+ name: 'Cyrillic Characters - Legitimate i18n',
389
+ description: `Found ${homoglyphs.length} Cyrillic character(s) in Cyrillic text context`,
390
+ category: 'unicode-stego',
391
+ severity: 'info',
392
+ passed: true,
393
+ message: `${homoglyphs.length} Cyrillic characters are in legitimate Cyrillic text blocks (translations, i18n badges)`,
394
+ fixable: false,
395
+ file: ast.artifactPath,
396
+ attackClass: 'UNICODE-STEGO',
397
+ confidence: 0.93,
398
+ evidence: `Cyrillic in i18n context at lines: ${homoglyphs.slice(0, 5).map(c => `L${c.line}`).join(', ')}`,
399
+ });
400
+ }
401
+ else {
402
+ // Cyrillic chars mixed into Latin words (homoglyph attack)
403
+ findings.push({
404
+ checkId: 'UNICODE-STEGO-005',
405
+ name: 'Homoglyph Attack Detected',
406
+ description: `Found ${attacks.length} Cyrillic character(s) embedded in Latin words`,
407
+ category: 'unicode-stego',
408
+ severity: 'high',
409
+ passed: false,
410
+ message: `${attacks.length} Cyrillic homoglyphs mixed into Latin words - likely typosquatting or identifier spoofing`,
411
+ fixable: false,
412
+ file: ast.artifactPath,
413
+ attackClass: 'UNICODE-STEGO',
414
+ confidence: 0.88,
415
+ evidence: `Homoglyphs at lines: ${attacks.slice(0, 5).map(c => `U+${c.codepoint.toString(16).toUpperCase()} at L${c.line}`).join(', ')}`,
416
+ });
417
+ }
418
+ }
419
+ // ── Zero-Width Spaces (always suspicious unless very few) ──
420
+ if (zwsChars.length > 0) {
421
+ findings.push({
422
+ checkId: 'UNICODE-STEGO-001',
423
+ name: 'Zero-Width Spaces Detected',
424
+ description: `Found ${zwsChars.length} zero-width space(s)`,
425
+ category: 'unicode-stego',
426
+ severity: zwsChars.length > 10 ? 'critical' : 'high',
427
+ passed: false,
428
+ message: `${zwsChars.length} zero-width space characters detected - may encode hidden data`,
429
+ fixable: false,
430
+ file: ast.artifactPath,
431
+ attackClass: 'UNICODE-STEGO',
432
+ confidence: 0.85,
433
+ evidence: `ZWS at lines: ${zwsChars.slice(0, 5).map(c => `L${c.line}`).join(', ')}`,
434
+ });
435
+ }
436
+ // ── Bidi Overrides (almost always suspicious in configs/code) ──
437
+ if (bidiChars.length > 0) {
438
+ // Check if file has significant Arabic/Hebrew content (where bidi is normal)
439
+ const allCps = [...text].map(c => c.codePointAt(0));
440
+ const arabicCount = allCps.filter(c => isArabic(c)).length;
441
+ const hebrewCount = allCps.filter(c => c >= 0x0590 && c <= 0x05FF).length;
442
+ const rtlTextRatio = (arabicCount + hebrewCount) / Math.max(allCps.length, 1);
443
+ if (rtlTextRatio > 0.1) {
444
+ // Significant RTL text present - bidi overrides might be legitimate
445
+ findings.push({
446
+ checkId: 'UNICODE-STEGO-001',
447
+ name: 'Bidi Controls in RTL Text',
448
+ description: `Found ${bidiChars.length} bidi control(s) in content with ${Math.round(rtlTextRatio * 100)}% RTL text`,
449
+ category: 'unicode-stego',
450
+ severity: 'medium',
451
+ passed: true,
452
+ message: `Bidi controls in document with significant Arabic/Hebrew text - likely legitimate`,
453
+ fixable: false,
454
+ file: ast.artifactPath,
455
+ attackClass: 'UNICODE-STEGO',
456
+ confidence: 0.75,
457
+ evidence: 'RTL text detected, bidi controls may be required for correct rendering',
458
+ });
459
+ }
460
+ else {
461
+ findings.push({
462
+ checkId: 'UNICODE-STEGO-001',
463
+ name: 'Bidi Override Attack',
464
+ description: `Found ${bidiChars.length} bidi override(s) in non-RTL content`,
465
+ category: 'unicode-stego',
466
+ severity: 'critical',
467
+ passed: false,
468
+ message: `${bidiChars.length} bidi overrides in content without RTL text - likely text direction attack`,
469
+ fixable: false,
470
+ file: ast.artifactPath,
471
+ attackClass: 'UNICODE-STEGO',
472
+ confidence: 0.92,
473
+ evidence: `Bidi chars at lines: ${bidiChars.slice(0, 5).map(c => `U+${c.codepoint.toString(16).toUpperCase()} at L${c.line}`).join(', ')}`,
474
+ });
475
+ }
476
+ }
477
+ // ── Tag Characters (almost always malicious) ──
478
+ if (tagChars.length > 0) {
479
+ findings.push({
480
+ checkId: 'UNICODE-STEGO-004',
481
+ name: 'Tag Character Encoding',
482
+ description: `Found ${tagChars.length} tag character(s)`,
483
+ category: 'unicode-stego',
484
+ severity: 'critical',
485
+ passed: false,
486
+ message: `${tagChars.length} Unicode tag characters detected - these encode hidden ASCII data`,
487
+ fixable: false,
488
+ file: ast.artifactPath,
489
+ attackClass: 'UNICODE-STEGO',
490
+ confidence: 0.95,
491
+ evidence: `Tag chars at lines: ${tagChars.slice(0, 5).map(c => `L${c.line}`).join(', ')}`,
492
+ });
493
+ }
494
+ // ── Soft Hyphens (suspicious in configs, benign in prose) ──
495
+ if (softHyphens.length > 0) {
496
+ const inCode = ast.artifactType === 'source_code' || ast.artifactType === 'mcp_config';
497
+ findings.push({
498
+ checkId: 'UNICODE-STEGO-001',
499
+ name: 'Soft Hyphens Detected',
500
+ description: `Found ${softHyphens.length} soft hyphen(s)`,
501
+ category: 'unicode-stego',
502
+ severity: inCode ? 'high' : 'medium',
503
+ passed: !inCode && softHyphens.length < 5,
504
+ message: inCode
505
+ ? `${softHyphens.length} soft hyphens in code/config - may hide commands`
506
+ : `${softHyphens.length} soft hyphens in document (may be legitimate hyphenation)`,
507
+ fixable: false,
508
+ file: ast.artifactPath,
509
+ attackClass: 'UNICODE-STEGO',
510
+ confidence: inCode ? 0.85 : 0.6,
511
+ evidence: `Soft hyphens at lines: ${softHyphens.slice(0, 5).map(c => `L${c.line}`).join(', ')}`,
512
+ });
513
+ }
514
+ // ── Word Joiners (suspicious in configs) ──
515
+ if (wordJoiners.length > 0) {
516
+ findings.push({
517
+ checkId: 'UNICODE-STEGO-001',
518
+ name: 'Word Joiners Detected',
519
+ description: `Found ${wordJoiners.length} word joiner(s)`,
520
+ category: 'unicode-stego',
521
+ severity: wordJoiners.length > 5 ? 'high' : 'medium',
522
+ passed: false,
523
+ message: `${wordJoiners.length} word joiner characters detected - may encode hidden data`,
524
+ fixable: false,
525
+ file: ast.artifactPath,
526
+ attackClass: 'UNICODE-STEGO',
527
+ confidence: 0.80,
528
+ evidence: `Word joiners at lines: ${wordJoiners.slice(0, 5).map(c => `L${c.line}`).join(', ')}`,
529
+ });
530
+ }
531
+ return findings;
532
+ }
533
+ //# sourceMappingURL=stego-analyzer.js.map