@sparrowai/sparrow-mcp 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/scripts/mcp-test/mcp-client.js +1 -151
  3. package/dist/scripts/test-runner/standalone-test.js +1 -284
  4. package/dist/src/config/appConfig.js +1 -82
  5. package/dist/src/config/constants.js +1 -26
  6. package/dist/src/controllers/__tests__/analysis.controller.test.js +1 -202
  7. package/dist/src/controllers/__tests__/sast.controller.test.js +1 -126
  8. package/dist/src/controllers/__tests__/sca.controller.test.js +1 -120
  9. package/dist/src/controllers/__tests__/security.controller.test.js +1 -59
  10. package/dist/src/controllers/__tests__/system.controller.test.js +1 -19
  11. package/dist/src/controllers/analysis.controller.js +1 -440
  12. package/dist/src/controllers/sast.controller.js +1 -169
  13. package/dist/src/controllers/sca.controller.js +1 -180
  14. package/dist/src/controllers/security.controller.js +1 -63
  15. package/dist/src/controllers/system.controller.js +1 -2
  16. package/dist/src/schemas/tool.schemas.js +1 -58
  17. package/dist/src/server.js +1 -151
  18. package/dist/src/services/__tests__/checker.service.test.js +1 -83
  19. package/dist/src/services/__tests__/llm.service.test.js +1 -83
  20. package/dist/src/services/__tests__/s3.service.test.js +1 -77
  21. package/dist/src/services/__tests__/sparrow.service.test.js +1 -66
  22. package/dist/src/services/analysis.service.d.ts +0 -39
  23. package/dist/src/services/analysis.service.js +1 -790
  24. package/dist/src/services/checker.service.d.ts +0 -49
  25. package/dist/src/services/checker.service.js +1 -242
  26. package/dist/src/services/llm/AnthropicProvider.js +1 -20
  27. package/dist/src/services/llm/BedrockProvider.js +1 -48
  28. package/dist/src/services/llm/GeminiProvider.js +1 -15
  29. package/dist/src/services/llm/LLMFactory.js +1 -33
  30. package/dist/src/services/llm/LLMProvider.js +1 -1
  31. package/dist/src/services/llm/OllamaProvider.js +1 -35
  32. package/dist/src/services/llm/OpenAIProvider.js +1 -16
  33. package/dist/src/services/llm.service.js +1 -128
  34. package/dist/src/services/s3.service.d.ts +0 -44
  35. package/dist/src/services/s3.service.js +1 -891
  36. package/dist/src/services/sparrow.service.d.ts +0 -19
  37. package/dist/src/services/sparrow.service.js +1 -351
  38. package/dist/src/types/types.d.ts +0 -11
  39. package/dist/src/types/types.js +1 -1
  40. package/dist/src/utils/__tests__/crypto.util.test.js +1 -52
  41. package/dist/src/utils/__tests__/diff.util.test.js +1 -60
  42. package/dist/src/utils/__tests__/fileManager.test.js +1 -53
  43. package/dist/src/utils/__tests__/fileManagerSecure.test.js +1 -51
  44. package/dist/src/utils/__tests__/logger.test.js +1 -51
  45. package/dist/src/utils/crypto.util.d.ts +0 -33
  46. package/dist/src/utils/crypto.util.js +1 -59
  47. package/dist/src/utils/diff.util.js +1 -113
  48. package/dist/src/utils/fileManager.d.ts +0 -12
  49. package/dist/src/utils/fileManager.js +1 -34
  50. package/dist/src/utils/fileManagerSecure.d.ts +0 -12
  51. package/dist/src/utils/fileManagerSecure.js +1 -42
  52. package/dist/src/utils/logger.js +1 -56
  53. package/package.json +5 -3
@@ -1,151 +1 @@
1
- import { spawn } from 'child_process';
2
- import { createInterface } from 'readline';
3
- export class McpClient {
4
- serverCommand;
5
- serverArgs;
6
- process = null;
7
- messageId = 0;
8
- pendingRequests = new Map();
9
- notificationHandlers = new Map();
10
- isInitialized = false;
11
- rl = null;
12
- constructor(serverCommand, serverArgs) {
13
- this.serverCommand = serverCommand;
14
- this.serverArgs = serverArgs;
15
- }
16
- async start() {
17
- console.log(`[MCP Client] Starting server: ${this.serverCommand} ${this.serverArgs.join(' ')}`);
18
- // Set MCP_STDIO=true to suppress server logs on stdout (see utils/logger.ts)
19
- this.process = spawn(this.serverCommand, this.serverArgs, {
20
- stdio: ['pipe', 'pipe', 'pipe'],
21
- shell: true,
22
- env: { ...process.env, MCP_STDIO: 'true' }
23
- });
24
- if (!this.process.stdout || !this.process.stdin || !this.process.stderr) {
25
- throw new Error('Failed to spawn server process with stdio');
26
- }
27
- this.process.stderr.on('data', (data) => {
28
- console.error(`[Server Error] ${data.toString()}`);
29
- });
30
- this.rl = createInterface({ input: this.process.stdout });
31
- this.rl.on('line', (line) => {
32
- if (!line.trim())
33
- return;
34
- try {
35
- const message = JSON.parse(line);
36
- this.handleMessage(message);
37
- }
38
- catch (error) {
39
- console.log('[Server Log]', line);
40
- }
41
- });
42
- this.process.on('error', (err) => console.error('[MCP Client] Process error:', err));
43
- this.process.on('close', (code) => {
44
- console.log(`[MCP Client] Server closed with code ${code}`);
45
- if (this.rl)
46
- this.rl.close();
47
- for (const [id, handler] of this.pendingRequests.entries()) {
48
- handler(null, new Error('Server closed connection'));
49
- }
50
- this.pendingRequests.clear();
51
- });
52
- try {
53
- await this.initialize();
54
- }
55
- catch (e) {
56
- console.error('[MCP Client] Initialization failed:', e);
57
- throw e;
58
- }
59
- }
60
- async stop() {
61
- if (this.process) {
62
- if (this.rl)
63
- this.rl.close();
64
- try {
65
- if (process.platform === 'win32' && this.process.pid) {
66
- spawn('taskkill', ['/pid', this.process.pid.toString(), '/f', '/t']);
67
- }
68
- this.process.kill();
69
- }
70
- catch (e) { /* ignore */ }
71
- this.process = null;
72
- console.log('[MCP Client] Server stopped');
73
- }
74
- }
75
- async initialize() {
76
- console.log('[MCP Client] Sending initialize request...');
77
- const result = await this.request('initialize', {
78
- protocolVersion: '2024-11-05',
79
- capabilities: { roots: { listChanged: true } },
80
- clientInfo: { name: 'mcp-test-client', version: '1.0.0' }
81
- });
82
- console.log('[MCP Client] Initialized:', result);
83
- await this.notify('notifications/initialized', {});
84
- this.isInitialized = true;
85
- }
86
- async notify(method, params) {
87
- if (!this.process || this.process.exitCode !== null)
88
- return;
89
- const notification = { jsonrpc: '2.0', method, params };
90
- const json = JSON.stringify(notification);
91
- return new Promise((resolve, reject) => {
92
- try {
93
- const success = this.process.stdin.write(json + '\n');
94
- if (!success)
95
- console.warn('[MCP Client] stdin buffer full (notify)');
96
- resolve();
97
- }
98
- catch (e) {
99
- reject(e);
100
- }
101
- });
102
- }
103
- async request(method, params) {
104
- if (!this.process || this.process.exitCode !== null)
105
- throw new Error('Server not running');
106
- const id = this.messageId++;
107
- const request = { jsonrpc: '2.0', id, method, params };
108
- return new Promise((resolve, reject) => {
109
- this.pendingRequests.set(id, (res, rej) => {
110
- if (res)
111
- resolve(res);
112
- else
113
- reject(rej);
114
- });
115
- const json = JSON.stringify(request);
116
- try {
117
- const success = this.process.stdin.write(json + '\n');
118
- if (!success) {
119
- console.warn('[MCP Client] stdin buffer full (request)');
120
- }
121
- }
122
- catch (e) {
123
- this.pendingRequests.delete(id);
124
- reject(e);
125
- }
126
- });
127
- }
128
- onNotification(method, handler) {
129
- this.notificationHandlers.set(method, handler);
130
- }
131
- handleMessage(message) {
132
- if (message.id !== undefined && (message.result !== undefined || message.error !== undefined)) {
133
- const handler = this.pendingRequests.get(message.id);
134
- if (handler) {
135
- this.pendingRequests.delete(message.id);
136
- if (message.error) {
137
- handler(null, new Error(`JSON-RPC Error: ${JSON.stringify(message.error)}`));
138
- }
139
- else {
140
- handler(message.result, null);
141
- }
142
- }
143
- }
144
- else if (message.method && !message.id) {
145
- const handler = this.notificationHandlers.get(message.method);
146
- if (handler) {
147
- handler(message.params);
148
- }
149
- }
150
- }
151
- }
1
+ function a0_0x419f(_0xa64055,_0x4e64c3){_0xa64055=_0xa64055-0x8c;const _0x2b9f69=a0_0x28ac();let _0x421b0a=_0x2b9f69[_0xa64055];return _0x421b0a;}const a0_0x4f593f=a0_0x419f;(function(_0x2121eb,_0x3c508e){const _0x5ee32f=a0_0x419f,_0x2d142e=_0x2121eb();while(!![]){try{const _0x528be7=parseInt(_0x5ee32f(0xc4))/0x1+parseInt(_0x5ee32f(0xd9))/0x2+parseInt(_0x5ee32f(0xca))/0x3*(parseInt(_0x5ee32f(0xaa))/0x4)+-parseInt(_0x5ee32f(0xea))/0x5*(parseInt(_0x5ee32f(0xdb))/0x6)+-parseInt(_0x5ee32f(0xa2))/0x7+parseInt(_0x5ee32f(0xc6))/0x8+parseInt(_0x5ee32f(0xa5))/0x9*(-parseInt(_0x5ee32f(0x8e))/0xa);if(_0x528be7===_0x3c508e)break;else _0x2d142e['push'](_0x2d142e['shift']());}catch(_0x5214c4){_0x2d142e['push'](_0x2d142e['shift']());}}}(a0_0x28ac,0xb5300));const a0_0x8f821d=(function(){let _0x34da39=!![];return function(_0x4a3e2b,_0x2025be){const _0x35dab2=_0x34da39?function(){const _0x2a0bef=a0_0x419f;if(_0x2025be){const _0x2cb22a=_0x2025be[_0x2a0bef(0x93)](_0x4a3e2b,arguments);return _0x2025be=null,_0x2cb22a;}}:function(){};return _0x34da39=![],_0x35dab2;};}()),a0_0x421b0a=a0_0x8f821d(this,function(){const _0x269a44=a0_0x419f;return a0_0x421b0a[_0x269a44(0xa4)]()[_0x269a44(0xb2)](_0x269a44(0xc1)+'+$')['toString']()[_0x269a44(0xb0)+'r'](a0_0x421b0a)['search'](_0x269a44(0xc1)+'+$');});a0_0x421b0a();import{spawn}from'child_process';import{createInterface}from'readline';function a0_0x28ac(){const _0x16788f=['MCP_STDIO','t]\x20Startin','85LaBzKw','ization\x20fa','Failed\x20to\x20','2024-11-05','stdin','initialize','roots','onNotifica','stdout','request','ons/initia','rror:\x20','t]\x20Server\x20','675330AyDdYN','serverArgs','[Server\x20Er','start','\x20running','apply','2.0','trim','exitCode','age','zed','sed\x20connec','error','AudlC','Server\x20clo','Server\x20not','t]\x20Sending','env','1.0.0','stringify','6515474tPBUWq','isInitiali','toString','144nqlQyX','shell','jsonrpc','ized:','[MCP\x20Clien','5060hJhKvS','params','vagUJ','mcp-test-c','pipe','INQom','constructo','log','search','t]\x20Process','write','stderr','set','handleMess','[Server\x20Lo','er\x20process','TQEAp','delete','clear','/pid','notify','stop','entries','(((.+)+)+)','spawn\x20serv','pendingReq','1179694gNjdOE','h\x20code\x20','9764472kXLscn','uests','\x20(request)','kill','1974EzTpeA','t]\x20stdin\x20b','lient','parse','\x20error:','and','version','join','ror]\x20','onHandlers','taskkill','t]\x20Initial','rsion','lized','method','910336gYJjUv','warn','329766bpAIDr','close','line','name','notificati','\x20with\x20stdi','serverComm','messageId','uffer\x20full','tion','process','clientInfo','pid'];a0_0x28ac=function(){return _0x16788f;};return a0_0x28ac();}export class McpClient{[a0_0x4f593f(0xe1)+a0_0x4f593f(0xcf)];['serverArgs'];[a0_0x4f593f(0xe5)]=null;['messageId']=0x0;['pendingReq'+a0_0x4f593f(0xc7)]=new Map();['notificati'+a0_0x4f593f(0xd3)]=new Map();[a0_0x4f593f(0xa3)+a0_0x4f593f(0x98)]=![];['rl']=null;constructor(_0x34ed0c,_0x5a39c6){const _0x2d5ec5=a0_0x4f593f;this[_0x2d5ec5(0xe1)+_0x2d5ec5(0xcf)]=_0x34ed0c,this[_0x2d5ec5(0x8f)]=_0x5a39c6;}async[a0_0x4f593f(0x91)](){const _0x15894a=a0_0x4f593f,_0x46f31a={'TQEAp':function(_0xd5a148,_0x4de97c){return _0xd5a148(_0x4de97c);},'xWYqt':_0x15894a(0x9a)};console[_0x15894a(0xb1)](_0x15894a(0xa9)+_0x15894a(0xe9)+'g\x20server:\x20'+this['serverComm'+_0x15894a(0xcf)]+'\x20'+this[_0x15894a(0x8f)][_0x15894a(0xd1)]('\x20'));const _0x376ef9={...process.env};_0x376ef9[_0x15894a(0xe8)]='true';const _0x1816bf={};_0x1816bf['stdio']=['pipe',_0x15894a(0xae),_0x15894a(0xae)],_0x1816bf[_0x15894a(0xa6)]=!![],_0x1816bf[_0x15894a(0x9f)]=_0x376ef9,this[_0x15894a(0xe5)]=spawn(this[_0x15894a(0xe1)+_0x15894a(0xcf)],this['serverArgs'],_0x1816bf);if(!this['process'][_0x15894a(0xf2)]||!this[_0x15894a(0xe5)][_0x15894a(0xee)]||!this[_0x15894a(0xe5)][_0x15894a(0xb5)])throw new Error(_0x15894a(0xec)+_0x15894a(0xc2)+_0x15894a(0xb9)+_0x15894a(0xe0)+'o');this[_0x15894a(0xe5)][_0x15894a(0xb5)]['on']('data',_0x4e11ff=>{const _0x483beb=_0x15894a;console[_0x483beb(0x9a)](_0x483beb(0x90)+_0x483beb(0xd2)+_0x4e11ff[_0x483beb(0xa4)]());}),this['rl']=_0x46f31a[_0x15894a(0xba)](createInterface,{'input':this[_0x15894a(0xe5)][_0x15894a(0xf2)]}),this['rl']['on'](_0x15894a(0xdd),_0x5d9501=>{const _0x3563ea=_0x15894a;if(!_0x5d9501[_0x3563ea(0x95)]())return;try{const _0x4b8c03=JSON[_0x3563ea(0xcd)](_0x5d9501);this[_0x3563ea(0xb7)+_0x3563ea(0x97)](_0x4b8c03);}catch(_0x3a470f){console['log'](_0x3563ea(0xb8)+'g]',_0x5d9501);}}),this[_0x15894a(0xe5)]['on'](_0x46f31a['xWYqt'],_0x210b8b=>console[_0x15894a(0x9a)](_0x15894a(0xa9)+_0x15894a(0xb3)+_0x15894a(0xce),_0x210b8b)),this[_0x15894a(0xe5)]['on'](_0x15894a(0xdc),_0x56a727=>{const _0x38ff10=_0x15894a;console[_0x38ff10(0xb1)](_0x38ff10(0xa9)+'t]\x20Server\x20'+'closed\x20wit'+_0x38ff10(0xc5)+_0x56a727);if(this['rl'])this['rl'][_0x38ff10(0xdc)]();for(const [_0xb987d0,_0x540a61]of this[_0x38ff10(0xc3)+'uests'][_0x38ff10(0xc0)]()){_0x540a61(null,new Error(_0x38ff10(0x9c)+_0x38ff10(0x99)+_0x38ff10(0xe4)));}this[_0x38ff10(0xc3)+_0x38ff10(0xc7)][_0x38ff10(0xbc)]();});try{await this['initialize']();}catch(_0xaeafd3){console[_0x15894a(0x9a)](_0x15894a(0xa9)+_0x15894a(0xd5)+_0x15894a(0xeb)+'iled:',_0xaeafd3);throw _0xaeafd3;}}async[a0_0x4f593f(0xbf)](){const _0x3cffdb=a0_0x4f593f,_0x4e5623={};_0x4e5623['INQom']=_0x3cffdb(0xa9)+_0x3cffdb(0x8d)+'stopped';const _0x4807d1=_0x4e5623;if(this[_0x3cffdb(0xe5)]){if(this['rl'])this['rl'][_0x3cffdb(0xdc)]();try{process['platform']==='win32'&&this['process'][_0x3cffdb(0xe7)]&&spawn(_0x3cffdb(0xd4),[_0x3cffdb(0xbd),this[_0x3cffdb(0xe5)][_0x3cffdb(0xe7)][_0x3cffdb(0xa4)](),'/f','/t']),this['process'][_0x3cffdb(0xc9)]();}catch(_0x374a33){}this[_0x3cffdb(0xe5)]=null,console[_0x3cffdb(0xb1)](_0x4807d1[_0x3cffdb(0xaf)]);}}async[a0_0x4f593f(0xef)](){const _0x3cf6dd=a0_0x4f593f,_0x42a5e1={};_0x42a5e1[_0x3cf6dd(0x9b)]='[MCP\x20Clien'+_0x3cf6dd(0xd5)+_0x3cf6dd(0xa8);const _0x37ef0e=_0x42a5e1;console['log'](_0x3cf6dd(0xa9)+_0x3cf6dd(0x9e)+'\x20initializ'+'e\x20request.'+'..');const _0x34efdb={};_0x34efdb['listChange'+'d']=!![];const _0xa4e013={};_0xa4e013[_0x3cf6dd(0xf0)]=_0x34efdb;const _0x43792b={};_0x43792b[_0x3cf6dd(0xde)]=_0x3cf6dd(0xad)+_0x3cf6dd(0xcc),_0x43792b[_0x3cf6dd(0xd0)]=_0x3cf6dd(0xa0);const _0x475fe4={};_0x475fe4['protocolVe'+_0x3cf6dd(0xd6)]=_0x3cf6dd(0xed),_0x475fe4['capabiliti'+'es']=_0xa4e013,_0x475fe4[_0x3cf6dd(0xe6)]=_0x43792b;const _0x16004d=await this[_0x3cf6dd(0xf3)](_0x3cf6dd(0xef),_0x475fe4);console[_0x3cf6dd(0xb1)](_0x37ef0e[_0x3cf6dd(0x9b)],_0x16004d),await this[_0x3cf6dd(0xbe)](_0x3cf6dd(0xdf)+_0x3cf6dd(0xf4)+_0x3cf6dd(0xd7),{}),this['isInitiali'+'zed']=!![];}async[a0_0x4f593f(0xbe)](_0x4c8616,_0x58c786){const _0x54fb68=a0_0x4f593f,_0x5305ed={};_0x5305ed[_0x54fb68(0xac)]=function(_0x27982f,_0x4d1381){return _0x27982f+_0x4d1381;};const _0x1b14d0=_0x5305ed;if(!this[_0x54fb68(0xe5)]||this[_0x54fb68(0xe5)]['exitCode']!==null)return;const _0x407b0e={};_0x407b0e[_0x54fb68(0xa7)]='2.0',_0x407b0e[_0x54fb68(0xd8)]=_0x4c8616,_0x407b0e[_0x54fb68(0xab)]=_0x58c786;const _0x2c5851=_0x407b0e,_0x4f6e49=JSON['stringify'](_0x2c5851);return new Promise((_0x2729e3,_0x3862ac)=>{const _0x4c96ae=_0x54fb68;try{const _0x3a00c1=this[_0x4c96ae(0xe5)][_0x4c96ae(0xee)][_0x4c96ae(0xb4)](_0x1b14d0[_0x4c96ae(0xac)](_0x4f6e49,'\x0a'));if(!_0x3a00c1)console['warn'](_0x4c96ae(0xa9)+_0x4c96ae(0xcb)+_0x4c96ae(0xe3)+'\x20(notify)');_0x2729e3();}catch(_0x4b4475){_0x3862ac(_0x4b4475);}});}async[a0_0x4f593f(0xf3)](_0x447749,_0xf53874){const _0x2d5e2b=a0_0x4f593f;if(!this[_0x2d5e2b(0xe5)]||this[_0x2d5e2b(0xe5)][_0x2d5e2b(0x96)]!==null)throw new Error(_0x2d5e2b(0x9d)+_0x2d5e2b(0x92));const _0x2d7f94=this[_0x2d5e2b(0xe2)]++,_0x3e5520={};_0x3e5520[_0x2d5e2b(0xa7)]=_0x2d5e2b(0x94),_0x3e5520['id']=_0x2d7f94,_0x3e5520['method']=_0x447749,_0x3e5520[_0x2d5e2b(0xab)]=_0xf53874;const _0x1a6d0d=_0x3e5520;return new Promise((_0x449bd0,_0x45175b)=>{const _0x131c83=_0x2d5e2b;this[_0x131c83(0xc3)+_0x131c83(0xc7)][_0x131c83(0xb6)](_0x2d7f94,(_0x365b7e,_0x25cde6)=>{if(_0x365b7e)_0x449bd0(_0x365b7e);else _0x45175b(_0x25cde6);});const _0x3271b9=JSON[_0x131c83(0xa1)](_0x1a6d0d);try{const _0x2b70a2=this[_0x131c83(0xe5)][_0x131c83(0xee)][_0x131c83(0xb4)](_0x3271b9+'\x0a');!_0x2b70a2&&console[_0x131c83(0xda)](_0x131c83(0xa9)+_0x131c83(0xcb)+'uffer\x20full'+_0x131c83(0xc8));}catch(_0x4abcef){this['pendingReq'+_0x131c83(0xc7)][_0x131c83(0xbb)](_0x2d7f94),_0x45175b(_0x4abcef);}});}[a0_0x4f593f(0xf1)+a0_0x4f593f(0xe4)](_0x51d6ca,_0x5287f8){const _0x149d31=a0_0x4f593f;this[_0x149d31(0xdf)+_0x149d31(0xd3)][_0x149d31(0xb6)](_0x51d6ca,_0x5287f8);}[a0_0x4f593f(0xb7)+a0_0x4f593f(0x97)](_0x58511d){const _0x59179c=a0_0x4f593f,_0x19e771={};_0x19e771['ekcJX']=function(_0x5ada56,_0x1933ad){return _0x5ada56!==_0x1933ad;};const _0xb75627=_0x19e771;if(_0xb75627['ekcJX'](_0x58511d['id'],undefined)&&(_0x58511d['result']!==undefined||_0x58511d[_0x59179c(0x9a)]!==undefined)){const _0x249531=this[_0x59179c(0xc3)+_0x59179c(0xc7)]['get'](_0x58511d['id']);_0x249531&&(this[_0x59179c(0xc3)+_0x59179c(0xc7)]['delete'](_0x58511d['id']),_0x58511d[_0x59179c(0x9a)]?_0x249531(null,new Error('JSON-RPC\x20E'+_0x59179c(0x8c)+JSON[_0x59179c(0xa1)](_0x58511d[_0x59179c(0x9a)]))):_0x249531(_0x58511d['result'],null));}else{if(_0x58511d['method']&&!_0x58511d['id']){const _0x413f00=this[_0x59179c(0xdf)+_0x59179c(0xd3)]['get'](_0x58511d[_0x59179c(0xd8)]);_0x413f00&&_0x413f00(_0x58511d[_0x59179c(0xab)]);}}}}
@@ -1,284 +1 @@
1
- import { McpClient } from '../mcp-test/mcp-client.js';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
- // ESM 환경에서 __dirname 정의
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
- // ============================================
9
- // Configuration
10
- // ============================================
11
- const SPARROW_BASE_URL = process.env.SPARROW_API_URL || 'https://ondemand.sparrowcloud.ai';
12
- const MAX_POLL_ATTEMPTS = 300; // 15분
13
- const POLL_INTERVAL_MS = 3000;
14
- /**
15
- * 분석 링크 생성
16
- */
17
- function getAnalysisLink(analysisId) {
18
- return `${SPARROW_BASE_URL}/ko/requestList/analysis/${analysisId}`;
19
- }
20
- /**
21
- * 분석 완료까지 대기 (MCP tool 사용)
22
- */
23
- async function pollUntilComplete(client, analysisId) {
24
- let attempts = 0;
25
- while (attempts < MAX_POLL_ATTEMPTS) {
26
- const statusResult = await client.request('tools/call', {
27
- name: 'get_analysis_status',
28
- arguments: { analysisId }
29
- });
30
- const statusUpper = String(statusResult.content[0].text).toUpperCase();
31
- if (statusUpper.includes('COMPLETE') || statusUpper.includes('SUCCESS') || statusUpper.includes('FAIL') || statusUpper.includes('STOP')) {
32
- return { status: 'COMPLETE', result: statusResult.content[0].text };
33
- }
34
- await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
35
- attempts++;
36
- }
37
- throw new Error(`Polling timeout after ${attempts} attempts`);
38
- }
39
- /**
40
- * TSV 필드 이스케이프
41
- */
42
- function escapeTsvField(value, preserveNewlines = false) {
43
- if (!value) {
44
- return '""';
45
- }
46
- let cleaned = value;
47
- if (preserveNewlines) {
48
- cleaned = cleaned.replace(/\r\n/g, '\n');
49
- cleaned = cleaned.replace(/\t/g, ' ');
50
- }
51
- else {
52
- cleaned = cleaned.replace(/\r?\n/g, ' ');
53
- cleaned = cleaned.replace(/\t/g, ' ');
54
- cleaned = cleaned.replace(/\s+/g, ' ');
55
- cleaned = cleaned.trim();
56
- }
57
- cleaned = cleaned.replace(/"/g, '""');
58
- return `"${cleaned}"`;
59
- }
60
- /**
61
- * TSV 저장
62
- */
63
- function saveTsv(results, outputPath) {
64
- const headers = [
65
- '테스트ID', '도구명', '테스트분류', '테스트명', '사전조건', '테스트절차',
66
- '기대결과', '테스트데이터', '우선순위', '상태', '담당자', '결과',
67
- '판단근거', '분석 링크', '비고'
68
- ];
69
- const lines = [headers.map(h => escapeTsvField(h)).join('\t')];
70
- for (const [tcId, result] of Object.entries(results)) {
71
- const testCase = BUILTIN_TESTS.find(t => t.id === tcId);
72
- if (!testCase)
73
- continue;
74
- const row = [
75
- tcId,
76
- testCase.tool,
77
- testCase.category,
78
- testCase.name,
79
- testCase.precondition || '',
80
- testCase.procedure,
81
- testCase.expected,
82
- testCase.data || '',
83
- testCase.priority,
84
- '완료',
85
- 'Antigravity',
86
- result.status,
87
- result.rationale,
88
- result.analysisLink || '',
89
- ''
90
- ];
91
- lines.push(row.map((v, idx) => escapeTsvField(v, idx === 5)).join('\t')); // 테스트절차(idx=5)는 줄바꿈 유지
92
- }
93
- fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8');
94
- }
95
- // ============================================
96
- // Builtin Test Files (작은 파일만)
97
- // ============================================
98
- const BUILTIN_JAVA_CODE = `public class UserController {
99
- public void login(String username, String password) {
100
- String query = "SELECT * FROM users WHERE username='" + username + "'";
101
- }
102
- }`;
103
- const BUILTIN_JS_CODE = `const express = require('express');
104
- const app = express();
105
- app.get('/user', (req, res) => {
106
- const userId = req.query.id;
107
- const query = "SELECT * FROM users WHERE id=" + userId;
108
- res.send(query);
109
- });`;
110
- // ============================================
111
- // Builtin Test Cases (MCP Client 사용)
112
- // ============================================
113
- const BUILTIN_TESTS = [
114
- {
115
- id: 'TC001',
116
- tool: 'analyze_file_sast',
117
- category: '정상동작',
118
- name: '단일 Java 파일 SAST 분석 성공',
119
- procedure: '1. fileContent에 Java 코드 입력\n2. fileName에 UserController.java 입력\n3. 도구 실행',
120
- expected: 'analysisId 반환\nstatus: INIT',
121
- data: `fileContent: "${BUILTIN_JAVA_CODE}"\nfileName: "UserController.java"`,
122
- priority: '높음',
123
- task: async (client) => {
124
- // MCP tool 호출
125
- const result = await client.request('tools/call', {
126
- name: 'analyze_file_sast',
127
- arguments: {
128
- fileContent: BUILTIN_JAVA_CODE,
129
- fileName: 'UserController.java'
130
- }
131
- });
132
- // analysisId 추출 (structuredContent 우선)
133
- let analysisId;
134
- if (result.structuredContent && result.structuredContent.analysisId) {
135
- analysisId = String(result.structuredContent.analysisId);
136
- }
137
- else {
138
- // 텍스트에서 추출
139
- const resultText = result.content[0].text;
140
- const idMatch = resultText.match(/Analysis ID:\s*(\d+)/i);
141
- if (!idMatch) {
142
- throw new Error(`Failed to extract analysisId from result: ${resultText}`);
143
- }
144
- analysisId = idMatch[1];
145
- }
146
- console.log(`[TC001] Analysis ID: ${analysisId}`);
147
- // 분석 완료 대기
148
- await pollUntilComplete(client, analysisId);
149
- // 결과 조회
150
- const analysisResult = await client.request('tools/call', {
151
- name: 'get_analysis_results',
152
- arguments: { analysisId }
153
- });
154
- const analysisText = analysisResult.content[0].text;
155
- const vulnMatch = analysisText.match(/(\d+)\s*vulnerabilities/i);
156
- const vulnCount = vulnMatch ? vulnMatch[1] : '0';
157
- return {
158
- passed: true,
159
- rationale: `Real Execution Verified (MCP): Analysis ${analysisId} completed. Found ${vulnCount} vulnerabilities.`,
160
- analysisLink: getAnalysisLink(analysisId)
161
- };
162
- }
163
- },
164
- {
165
- id: 'TC004',
166
- tool: 'analyze_file_sast',
167
- category: '정상동작',
168
- name: '지원 언어 확인 - JavaScript',
169
- procedure: '1. JavaScript 파일 내용 입력\n2. fileName에 .js 확장자 사용',
170
- expected: '정상적으로 analysisId 반환',
171
- data: 'fileName: "index.js"',
172
- priority: '중간',
173
- task: async (client) => {
174
- const result = await client.request('tools/call', {
175
- name: 'analyze_file_sast',
176
- arguments: {
177
- fileContent: BUILTIN_JS_CODE,
178
- fileName: 'index.js'
179
- }
180
- });
181
- // analysisId 추출 (structuredContent 우선)
182
- let analysisId;
183
- if (result.structuredContent && result.structuredContent.analysisId) {
184
- analysisId = String(result.structuredContent.analysisId);
185
- }
186
- else {
187
- const resultText = result.content[0].text;
188
- const idMatch = resultText.match(/Analysis ID:\s*(\d+)/i);
189
- if (!idMatch) {
190
- throw new Error(`Failed to extract analysisId from result: ${resultText}`);
191
- }
192
- analysisId = idMatch[1];
193
- }
194
- console.log(`[TC004] Analysis ID: ${analysisId}`);
195
- return {
196
- passed: true,
197
- rationale: `Real Execution Verified (MCP): Analysis ${analysisId} initiated for JavaScript file.`,
198
- analysisLink: getAnalysisLink(analysisId)
199
- };
200
- }
201
- },
202
- ];
203
- // ============================================
204
- // Test Runner
205
- // ============================================
206
- async function runTest(client, testCase) {
207
- console.log(`\n[${testCase.id}] Running: ${testCase.name}...`);
208
- const startTime = Date.now();
209
- try {
210
- const { passed, rationale, analysisLink } = await testCase.task(client);
211
- const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
212
- const status = passed ? '통과' : '실패';
213
- console.log(`[${testCase.id}] ${status} (${elapsed}s)`);
214
- return { status, rationale, analysisLink };
215
- }
216
- catch (error) {
217
- const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
218
- console.error(`[${testCase.id}] ERROR (${elapsed}s):`, error.message);
219
- return {
220
- status: '실패',
221
- rationale: `EXECUTION ERROR: ${error.message}`
222
- };
223
- }
224
- }
225
- async function runTests(serverCommand, serverArgs) {
226
- console.log('╔═══════════════════════════════════════════════════════════════╗');
227
- console.log('║ Sparrow MCP - 자동화 테스트 (MCP Client) ║');
228
- console.log('╚═══════════════════════════════════════════════════════════════╝\n');
229
- console.log(`내장 테스트 케이스: ${BUILTIN_TESTS.length}개`);
230
- console.log(`최대 대기 시간: ${(MAX_POLL_ATTEMPTS * POLL_INTERVAL_MS) / 60000}분`);
231
- console.log(`MCP 서버: ${serverCommand} ${serverArgs.join(' ')}\n`);
232
- // MCP 클라이언트 시작
233
- const client = new McpClient(serverCommand, serverArgs);
234
- try {
235
- await client.start();
236
- console.log('✅ MCP 서버 연결 성공\n');
237
- const results = {};
238
- for (const testCase of BUILTIN_TESTS) {
239
- const result = await runTest(client, testCase);
240
- results[testCase.id] = result;
241
- }
242
- return results;
243
- }
244
- finally {
245
- await client.stop();
246
- console.log('\n✅ MCP 서버 연결 종료');
247
- }
248
- }
249
- // ============================================
250
- // Main Entry Point
251
- // ============================================
252
- async function main() {
253
- const outputPath = path.join(process.cwd(), 'sparrow-mcp-test-results.tsv');
254
- console.log('🧪 Sparrow MCP 자동 테스트 시작\n');
255
- if (!process.env.SPARROW_API_KEY) {
256
- console.warn('⚠️ 경고: SPARROW_API_KEY가 설정되지 않았습니다.');
257
- console.warn(' 설정 방법:');
258
- console.warn(' - Windows: $env:SPARROW_API_KEY = "your-key"');
259
- console.warn(' - Linux/Mac: export SPARROW_API_KEY="your-key"\n');
260
- }
261
- try {
262
- // 패키지 루트에서 서버 실행
263
- const packageRoot = path.resolve(__dirname, '../../..');
264
- const serverPath = path.join(packageRoot, 'dist', 'src', 'server.js');
265
- const results = await runTests('node', [serverPath]);
266
- // 결과 저장
267
- saveTsv(results, outputPath);
268
- // 요약 출력
269
- const passed = Object.values(results).filter(r => r.status === '통과').length;
270
- const failed = Object.values(results).filter(r => r.status === '실패').length;
271
- console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
272
- console.log('✅ 테스트 완료');
273
- console.log(` 통과: ${passed}/${passed + failed}`);
274
- console.log(` 실패: ${failed}`);
275
- console.log(`\n📄 결과 저장: ${outputPath}`);
276
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
277
- process.exit(failed > 0 ? 1 : 0);
278
- }
279
- catch (error) {
280
- console.error('❌ 테스트 실행 실패:', error.message);
281
- process.exit(1);
282
- }
283
- }
284
- main();
1
+ const a1_0x306c9a=a1_0x3826;(function(_0x1fa230,_0x5f59b3){const _0x26c785=a1_0x3826,_0x5b3bb6=_0x1fa230();while(!![]){try{const _0x45fcde=parseInt(_0x26c785(0x18d))/0x1+-parseInt(_0x26c785(0x190))/0x2*(parseInt(_0x26c785(0x20c))/0x3)+-parseInt(_0x26c785(0x1a8))/0x4+parseInt(_0x26c785(0x23e))/0x5+parseInt(_0x26c785(0x1c7))/0x6*(parseInt(_0x26c785(0x1ba))/0x7)+-parseInt(_0x26c785(0x229))/0x8+parseInt(_0x26c785(0x1c3))/0x9*(-parseInt(_0x26c785(0x22d))/0xa);if(_0x45fcde===_0x5f59b3)break;else _0x5b3bb6['push'](_0x5b3bb6['shift']());}catch(_0x142e9d){_0x5b3bb6['push'](_0x5b3bb6['shift']());}}}(a1_0x1122,0xb1ca8));import{McpClient}from'../mcp-test/mcp-client.js';function a1_0x3826(_0x51ca06,_0x48167e){_0x51ca06=_0x51ca06-0x187;const _0x21228e=a1_0x1122();let _0x2afe13=_0x21228e[_0x51ca06];return _0x2afe13;}import a1_0x1fbf95 from'fs';import a1_0x5da455 from'path';import{fileURLToPath}from'url';const __filename=fileURLToPath(import.meta.url),__dirname=a1_0x5da455['dirname'](__filename),SPARROW_BASE_URL=process.env.SPARROW_API_URL||'https://on'+a1_0x306c9a(0x215)+'rrowcloud.'+'ai',MAX_POLL_ATTEMPTS=0x12c,POLL_INTERVAL_MS=0xbb8;function getAnalysisLink(_0x4d6a4d){const _0x429b07=a1_0x306c9a;return SPARROW_BASE_URL+(_0x429b07(0x1db)+'tList/anal'+'ysis/')+_0x4d6a4d;}async function pollUntilComplete(_0x42c526,_0x2038e6){const _0x4d4299=a1_0x306c9a;let _0x3db4d2=0x0;while(_0x3db4d2<MAX_POLL_ATTEMPTS){const _0x48c799={};_0x48c799[_0x4d4299(0x1fe)]=_0x2038e6;const _0x8e8be3={};_0x8e8be3[_0x4d4299(0x256)]='get_analys'+_0x4d4299(0x23f),_0x8e8be3[_0x4d4299(0x24a)]=_0x48c799;const _0x174c07=await _0x42c526[_0x4d4299(0x18a)](_0x4d4299(0x227),_0x8e8be3),_0x3e9a04=String(_0x174c07['content'][0x0][_0x4d4299(0x1c1)])[_0x4d4299(0x1b6)+'e']();if(_0x3e9a04[_0x4d4299(0x194)](_0x4d4299(0x1a4))||_0x3e9a04['includes'](_0x4d4299(0x243))||_0x3e9a04['includes'](_0x4d4299(0x223))||_0x3e9a04[_0x4d4299(0x194)](_0x4d4299(0x1e5))){const _0x3cb017={};return _0x3cb017[_0x4d4299(0x21e)]='COMPLETE',_0x3cb017[_0x4d4299(0x1d1)]=_0x174c07['content'][0x0]['text'],_0x3cb017;}await new Promise(_0x8bc5a8=>setTimeout(_0x8bc5a8,POLL_INTERVAL_MS)),_0x3db4d2++;}throw new Error(_0x4d4299(0x1dc)+_0x4d4299(0x24d)+'r\x20'+_0x3db4d2+_0x4d4299(0x257));}function a1_0x1122(){const _0x47ed96=['rationale','index.js','fileName','rom\x20result','Antigravit','⚠️\x20\x20경고:\x20SPA','troller.ja','t\x20app\x20=\x20ex','tent에\x20Java','...','ws:\x20$env:S','log','structured','\x20vulnerabi','analysisId','\x20\x20}\x0a}','\x20+\x20\x22\x27\x22;\x0a\x20\x20','server.js','EkiFX','start','════╗','테스트분류','ipt\x20파일\x20내용\x20','writeFileS','rId;\x0a\x20\x20\x20\x20r','우선순위','tool','fileName:\x20','191049GeIhPq','지원\x20언어\x20확인\x20-','error','았습니다.','message','EY가\x20설정되지\x20않','xGWzx','sername,\x20S','lities.','demand.spa','fileConten','ery);\x0a});','사전조건','search','✅\x20테스트\x20완료','\x20반환\x0astatus','p-test-res',']\x20Running:','status','rt\x20SPARROW','KmRBL','alysisId\x20f',']\x20ERROR\x20(','FAIL','constructo','toFixed','(((.+)+)+)','tools/call','\x20SAST\x20분석\x20성','2537400gVwzUm','테스트절차','join','filter','4971440fqRWlW','══════════','ults.tsv','\x20\x20\x20\x20public',',\x20res)\x20=>\x20','UserContro','ller.java','DxKjl','Failed\x20to\x20','cwd','fvNrW','s\x20WHERE\x20us','_API_KEY=\x22',':\x20\x22UserCon','resolve','replace','preconditi','5282260ppyiek','is_status','✅\x20MCP\x20서버\x20연','Client)\x20\x20\x20','le_sast','SUCCESS','\x20initiated','tion\x20Verif','ss\x20UserCon','analyze_fi','━━━━━━━━━━','\x20\x22SELECT\x20*','arguments','최대\x20대기\x20시간:\x20','t\x20userId\x20=','meout\x20afte','is_results','\x20JavaScrip','ync','now','\x20\x20\x20설정\x20방법:','word)\x20{\x0a\x20\x20','\x20코드\x20입력\x0a2.\x20','troller\x20{\x0a','name','\x20attempts','ire(\x27expre',':\x20INIT','public\x20cla','Name에\x20.js\x20','[TC004]\x20An','\x22index.js\x22','request','RROW_API_K','ur-key\x22','725856tSyyTY','\x20Analysis\x20','onst\x20query','2EfEKTf','ysisId\x20반환','도구명','ername=\x27\x22\x20','includes','판단근거','Real\x20Execu','va\x22','src','════╝\x0a','ied\x20(MCP):','\x20\x20\x20\x20\x20\x20Stri','내장\x20테스트\x20케이스','entries','sparrow-mc','map','warn','테스트명','utf-8','zLtnX','COMPLETE','\x20completed','담당자','ng\x20query\x20=','3957280lVhxGe','╔═════════','정상동작','alysis\x20ID:','\x20MCP\x20-\x20자동화','/Mac:\x20expo','cript\x20file','MCP\x20자동\x20테스트','task','extract\x20an','확장자\x20사용','TC001','\x0a✅\x20MCP\x20서버\x20','\x0a📄\x20결과\x20저장:\x20','toUpperCas','❌\x20테스트\x20실행\x20실','trim','length','70USLLnn','기대결과','\x20=\x20\x22SELECT','push','t:\x20\x22','tring\x20pass','const\x20expr','text','║\x20\x20\x20\x20\x20\x20\x20\x20\x20','18bcCSvS','_KEY\x20=\x20\x22yo','\x20테스트\x20(MCP\x20','fileName에\x20','786234SpapAo','pp.get(\x27/u','match','+\x20username','data','\x20\x20\x20-\x20Linux','\x20*\x20FROM\x20us','EXECUTION\x20','MCP\x20서버:\x20','priority','result','\x20req.query','🧪\x20Sparrow\x20','qPaOh','ller.java\x20','[TC001]\x20An','analysisLi','node','ser\x27,\x20(req','es.send(qu','/ko/reques','Polling\x20ti','Content','press();\x0aa','find','exit','분석\x20링크','expected','content','1.\x20JavaScr','STOP','n(String\x20u','1.\x20fileCon','get_analys','╚═════════','ERROR:\x20','입력\x0a3.\x20도구\x20실','테스트데이터','values','id=\x22\x20+\x20use','toString'];a1_0x1122=function(){return _0x47ed96;};return a1_0x1122();}function escapeTsvField(_0x29f8a8,_0x2e9efc=![]){const _0x2ea805=a1_0x306c9a;if(!_0x29f8a8)return'\x22\x22';let _0x1587ca=_0x29f8a8;return _0x2e9efc?(_0x1587ca=_0x1587ca[_0x2ea805(0x23c)](/\r\n/g,'\x0a'),_0x1587ca=_0x1587ca[_0x2ea805(0x23c)](/\t/g,'\x20')):(_0x1587ca=_0x1587ca[_0x2ea805(0x23c)](/\r?\n/g,'\x20'),_0x1587ca=_0x1587ca['replace'](/\t/g,'\x20'),_0x1587ca=_0x1587ca[_0x2ea805(0x23c)](/\s+/g,'\x20'),_0x1587ca=_0x1587ca[_0x2ea805(0x1b8)]()),_0x1587ca=_0x1587ca['replace'](/"/g,'\x22\x22'),'\x22'+_0x1587ca+'\x22';}function saveTsv(_0x2d931a,_0x8210c){const _0x278775=a1_0x306c9a,_0x541eaf={};_0x541eaf[_0x278775(0x1a3)]=_0x278775(0x1a6);const _0x4f58c9=_0x541eaf,_0x1ba315=['테스트ID',_0x278775(0x192),_0x278775(0x205),_0x278775(0x1a1),_0x278775(0x218),_0x278775(0x22a),_0x278775(0x1bb),_0x278775(0x1ec),_0x278775(0x209),'상태',_0x4f58c9[_0x278775(0x1a3)],'결과',_0x278775(0x195),_0x278775(0x1e1),'비고'],_0x2d2620=[_0x1ba315[_0x278775(0x19f)](_0x2660d1=>escapeTsvField(_0x2660d1))[_0x278775(0x22b)]('\x09')];for(const [_0x4b74a0,_0x47afcb]of Object[_0x278775(0x19d)](_0x2d931a)){const _0x233e39=BUILTIN_TESTS[_0x278775(0x1df)](_0x3f7fe2=>_0x3f7fe2['id']===_0x4b74a0);if(!_0x233e39)continue;const _0x3b9ee1=[_0x4b74a0,_0x233e39[_0x278775(0x20a)],_0x233e39['category'],_0x233e39[_0x278775(0x256)],_0x233e39[_0x278775(0x23d)+'on']||'',_0x233e39['procedure'],_0x233e39[_0x278775(0x1e2)],_0x233e39[_0x278775(0x1cb)]||'',_0x233e39[_0x278775(0x1d0)],'완료',_0x278775(0x1f4)+'y',_0x47afcb[_0x278775(0x21e)],_0x47afcb[_0x278775(0x1f0)],_0x47afcb[_0x278775(0x1d7)+'nk']||'',''];_0x2d2620[_0x278775(0x1bd)](_0x3b9ee1['map']((_0x108357,_0x245a0e)=>escapeTsvField(_0x108357,_0x245a0e===0x5))['join']('\x09'));}a1_0x1fbf95[_0x278775(0x207)+_0x278775(0x250)](_0x8210c,_0x2d2620[_0x278775(0x22b)]('\x0a'),_0x278775(0x1a2));}const BUILTIN_JAVA_CODE=a1_0x306c9a(0x25a)+a1_0x306c9a(0x246)+a1_0x306c9a(0x255)+a1_0x306c9a(0x230)+'\x20void\x20logi'+a1_0x306c9a(0x1e6)+a1_0x306c9a(0x213)+a1_0x306c9a(0x1bf)+a1_0x306c9a(0x253)+a1_0x306c9a(0x19b)+a1_0x306c9a(0x1a7)+a1_0x306c9a(0x249)+'\x20FROM\x20user'+a1_0x306c9a(0x238)+a1_0x306c9a(0x193)+a1_0x306c9a(0x1ca)+a1_0x306c9a(0x200)+a1_0x306c9a(0x1ff),BUILTIN_JS_CODE=a1_0x306c9a(0x1c0)+'ess\x20=\x20requ'+a1_0x306c9a(0x258)+'ss\x27);\x0acons'+a1_0x306c9a(0x1f7)+a1_0x306c9a(0x1de)+a1_0x306c9a(0x1c8)+a1_0x306c9a(0x1d9)+a1_0x306c9a(0x231)+'{\x0a\x20\x20\x20\x20cons'+a1_0x306c9a(0x24c)+a1_0x306c9a(0x1d2)+'.id;\x0a\x20\x20\x20\x20c'+a1_0x306c9a(0x18f)+a1_0x306c9a(0x1bc)+a1_0x306c9a(0x1cd)+'ers\x20WHERE\x20'+a1_0x306c9a(0x1ee)+a1_0x306c9a(0x208)+a1_0x306c9a(0x1da)+a1_0x306c9a(0x217),BUILTIN_TESTS=[{'id':a1_0x306c9a(0x1b3),'tool':'analyze_fi'+a1_0x306c9a(0x242),'category':a1_0x306c9a(0x1aa),'name':'단일\x20Java\x20파일'+a1_0x306c9a(0x228)+'공','procedure':a1_0x306c9a(0x1e7)+a1_0x306c9a(0x1f8)+a1_0x306c9a(0x254)+a1_0x306c9a(0x1c6)+a1_0x306c9a(0x232)+a1_0x306c9a(0x1d5)+a1_0x306c9a(0x1eb)+'행','expected':a1_0x306c9a(0x1fe)+a1_0x306c9a(0x21b)+a1_0x306c9a(0x259),'data':'fileConten'+a1_0x306c9a(0x1be)+BUILTIN_JAVA_CODE+('\x22\x0afileName'+a1_0x306c9a(0x23a)+a1_0x306c9a(0x1f6)+a1_0x306c9a(0x197)),'priority':'높음','task':async _0x50eb7b=>{const _0x2ad3f1=a1_0x306c9a,_0x553e03={};_0x553e03['fvNrW']='analyze_fi'+_0x2ad3f1(0x242),_0x553e03[_0x2ad3f1(0x234)]=_0x2ad3f1(0x227),_0x553e03['KmRBL']=_0x2ad3f1(0x1e8)+_0x2ad3f1(0x24e);const _0x232c9f=_0x553e03,_0x5fb8c3={};_0x5fb8c3[_0x2ad3f1(0x216)+'t']=BUILTIN_JAVA_CODE,_0x5fb8c3['fileName']=_0x2ad3f1(0x232)+_0x2ad3f1(0x233);const _0x3d38ab={};_0x3d38ab[_0x2ad3f1(0x256)]=_0x232c9f[_0x2ad3f1(0x237)],_0x3d38ab['arguments']=_0x5fb8c3;const _0x4ccff6=await _0x50eb7b[_0x2ad3f1(0x18a)](_0x2ad3f1(0x227),_0x3d38ab);let _0x46441d;if(_0x4ccff6[_0x2ad3f1(0x1fc)+'Content']&&_0x4ccff6[_0x2ad3f1(0x1fc)+'Content'][_0x2ad3f1(0x1fe)])_0x46441d=String(_0x4ccff6[_0x2ad3f1(0x1fc)+_0x2ad3f1(0x1dd)][_0x2ad3f1(0x1fe)]);else{const _0x3e80dd=_0x4ccff6[_0x2ad3f1(0x1e3)][0x0][_0x2ad3f1(0x1c1)],_0x4e48e6=_0x3e80dd[_0x2ad3f1(0x1c9)](/Analysis ID:\s*(\d+)/i);if(!_0x4e48e6)throw new Error(_0x2ad3f1(0x235)+'extract\x20an'+_0x2ad3f1(0x221)+_0x2ad3f1(0x1f3)+':\x20'+_0x3e80dd);_0x46441d=_0x4e48e6[0x1];}console[_0x2ad3f1(0x1fb)](_0x2ad3f1(0x1d6)+_0x2ad3f1(0x1ab)+'\x20'+_0x46441d),await pollUntilComplete(_0x50eb7b,_0x46441d);const _0x56a1cc={};_0x56a1cc[_0x2ad3f1(0x1fe)]=_0x46441d;const _0x471ac1=await _0x50eb7b[_0x2ad3f1(0x18a)](_0x232c9f[_0x2ad3f1(0x234)],{'name':_0x232c9f[_0x2ad3f1(0x220)],'arguments':_0x56a1cc}),_0x2e8e58=_0x471ac1[_0x2ad3f1(0x1e3)][0x0][_0x2ad3f1(0x1c1)],_0x39b07a=_0x2e8e58[_0x2ad3f1(0x1c9)](/(\d+)\s*vulnerabilities/i),_0x44437d=_0x39b07a?_0x39b07a[0x1]:'0';return{'passed':!![],'rationale':'Real\x20Execu'+_0x2ad3f1(0x245)+_0x2ad3f1(0x19a)+'\x20Analysis\x20'+_0x46441d+(_0x2ad3f1(0x1a5)+'.\x20Found\x20')+_0x44437d+(_0x2ad3f1(0x1fd)+_0x2ad3f1(0x214)),'analysisLink':getAnalysisLink(_0x46441d)};}},{'id':'TC004','tool':a1_0x306c9a(0x247)+a1_0x306c9a(0x242),'category':a1_0x306c9a(0x1aa),'name':a1_0x306c9a(0x20d)+a1_0x306c9a(0x24f)+'t','procedure':a1_0x306c9a(0x1e4)+a1_0x306c9a(0x206)+'입력\x0a2.\x20file'+a1_0x306c9a(0x187)+a1_0x306c9a(0x1b2),'expected':'정상적으로\x20anal'+a1_0x306c9a(0x191),'data':a1_0x306c9a(0x20b)+a1_0x306c9a(0x189),'priority':'중간','task':async _0xb4d4f3=>{const _0x5d87fe=a1_0x306c9a,_0x186e6c={};_0x186e6c[_0x5d87fe(0x216)+'t']=BUILTIN_JS_CODE,_0x186e6c[_0x5d87fe(0x1f2)]=_0x5d87fe(0x1f1);const _0x4d8dbe={};_0x4d8dbe[_0x5d87fe(0x256)]=_0x5d87fe(0x247)+'le_sast',_0x4d8dbe[_0x5d87fe(0x24a)]=_0x186e6c;const _0x160664=await _0xb4d4f3['request'](_0x5d87fe(0x227),_0x4d8dbe);let _0x3445dc;if(_0x160664['structured'+_0x5d87fe(0x1dd)]&&_0x160664['structured'+'Content'][_0x5d87fe(0x1fe)])_0x3445dc=String(_0x160664['structured'+'Content'][_0x5d87fe(0x1fe)]);else{const _0x1c37d2=_0x160664['content'][0x0][_0x5d87fe(0x1c1)],_0x21554d=_0x1c37d2[_0x5d87fe(0x1c9)](/Analysis ID:\s*(\d+)/i);if(!_0x21554d)throw new Error(_0x5d87fe(0x235)+_0x5d87fe(0x1b1)+'alysisId\x20f'+_0x5d87fe(0x1f3)+':\x20'+_0x1c37d2);_0x3445dc=_0x21554d[0x1];}return console['log'](_0x5d87fe(0x188)+_0x5d87fe(0x1ab)+'\x20'+_0x3445dc),{'passed':!![],'rationale':_0x5d87fe(0x196)+'tion\x20Verif'+'ied\x20(MCP):'+_0x5d87fe(0x18e)+_0x3445dc+(_0x5d87fe(0x244)+'\x20for\x20JavaS'+_0x5d87fe(0x1ae)+'.'),'analysisLink':getAnalysisLink(_0x3445dc)};}}];async function runTest(_0x5d8c6b,_0x5568d3){const _0x47564f=a1_0x306c9a;console[_0x47564f(0x1fb)]('\x0a['+_0x5568d3['id']+(_0x47564f(0x21d)+'\x20')+_0x5568d3[_0x47564f(0x256)]+_0x47564f(0x1f9));const _0x375bd9=Date[_0x47564f(0x251)]();try{const {passed:_0x225c4c,rationale:_0x2e071b,analysisLink:_0x1ff0e8}=await _0x5568d3[_0x47564f(0x1b0)](_0x5d8c6b),_0x33c9d6=((Date[_0x47564f(0x251)]()-_0x375bd9)/0x3e8)['toFixed'](0x1),_0x22adbb=_0x225c4c?'통과':'실패';console[_0x47564f(0x1fb)]('['+_0x5568d3['id']+']\x20'+_0x22adbb+'\x20('+_0x33c9d6+'s)');const _0x3fad75={};return _0x3fad75[_0x47564f(0x21e)]=_0x22adbb,_0x3fad75['rationale']=_0x2e071b,_0x3fad75[_0x47564f(0x1d7)+'nk']=_0x1ff0e8,_0x3fad75;}catch(_0x190d57){const _0x54cba3=((Date['now']()-_0x375bd9)/0x3e8)[_0x47564f(0x225)](0x1);console[_0x47564f(0x20e)]('['+_0x5568d3['id']+_0x47564f(0x222)+_0x54cba3+'s):',_0x190d57[_0x47564f(0x210)]);const _0x151663={};return _0x151663['status']='실패',_0x151663[_0x47564f(0x1f0)]=_0x47564f(0x1ce)+_0x47564f(0x1ea)+_0x190d57[_0x47564f(0x210)],_0x151663;}}async function runTests(_0x2bd46,_0x2728cb){const _0x3e9486=a1_0x306c9a,_0x5a8912={};_0x5a8912[_0x3e9486(0x212)]=_0x3e9486(0x1c2)+'\x20\x20\x20Sparrow'+_0x3e9486(0x1ac)+_0x3e9486(0x1c5)+_0x3e9486(0x241)+'\x20\x20\x20\x20\x20\x20\x20║';const _0x3ff002=_0x5a8912;console[_0x3e9486(0x1fb)](_0x3e9486(0x1a9)+'══════════'+_0x3e9486(0x22e)+_0x3e9486(0x22e)+'══════════'+'══════════'+_0x3e9486(0x204)),console[_0x3e9486(0x1fb)](_0x3ff002['xGWzx']),console[_0x3e9486(0x1fb)](_0x3e9486(0x1e9)+_0x3e9486(0x22e)+_0x3e9486(0x22e)+_0x3e9486(0x22e)+_0x3e9486(0x22e)+_0x3e9486(0x22e)+_0x3e9486(0x199)),console[_0x3e9486(0x1fb)](_0x3e9486(0x19c)+':\x20'+BUILTIN_TESTS[_0x3e9486(0x1b9)]+'개'),console['log'](_0x3e9486(0x24b)+MAX_POLL_ATTEMPTS*POLL_INTERVAL_MS/0xea60+'분'),console[_0x3e9486(0x1fb)](_0x3e9486(0x1cf)+_0x2bd46+'\x20'+_0x2728cb[_0x3e9486(0x22b)]('\x20')+'\x0a');const _0x4c1dfa=new McpClient(_0x2bd46,_0x2728cb);try{await _0x4c1dfa[_0x3e9486(0x203)](),console[_0x3e9486(0x1fb)](_0x3e9486(0x240)+'결\x20성공\x0a');const _0x6dca34={};for(const _0x1c428a of BUILTIN_TESTS){const _0x96e1fa=await runTest(_0x4c1dfa,_0x1c428a);_0x6dca34[_0x1c428a['id']]=_0x96e1fa;}return _0x6dca34;}finally{await _0x4c1dfa['stop'](),console[_0x3e9486(0x1fb)](_0x3e9486(0x1b4)+'연결\x20종료');}}async function main(){const _0x4cf5dc=a1_0x306c9a,_0x503f3a={'EkiFX':_0x4cf5dc(0x226)+'+$','qPaOh':function(_0x29117a,_0x3cb61a,_0x56a5a4){return _0x29117a(_0x3cb61a,_0x56a5a4);},'TdsGu':_0x4cf5dc(0x248)+_0x4cf5dc(0x248)+_0x4cf5dc(0x248)+_0x4cf5dc(0x248)+'━\x0a'},_0xaa3513=(function(){let _0x2618bd=!![];return function(_0x36ba7a,_0x3a20b3){const _0x4586e5=_0x2618bd?function(){if(_0x3a20b3){const _0x436e6a=_0x3a20b3['apply'](_0x36ba7a,arguments);return _0x3a20b3=null,_0x436e6a;}}:function(){};return _0x2618bd=![],_0x4586e5;};}()),_0x2fdc1f=_0x503f3a[_0x4cf5dc(0x1d4)](_0xaa3513,this,function(){const _0xf1cd64=_0x4cf5dc;return _0x2fdc1f['toString']()[_0xf1cd64(0x219)](_0xf1cd64(0x226)+'+$')[_0xf1cd64(0x1ef)]()[_0xf1cd64(0x224)+'r'](_0x2fdc1f)[_0xf1cd64(0x219)](_0x503f3a[_0xf1cd64(0x202)]);});_0x2fdc1f();const _0x3b7da1=a1_0x5da455['join'](process[_0x4cf5dc(0x236)](),_0x4cf5dc(0x19e)+_0x4cf5dc(0x21c)+_0x4cf5dc(0x22f));console[_0x4cf5dc(0x1fb)](_0x4cf5dc(0x1d3)+_0x4cf5dc(0x1af)+'\x20시작\x0a');!process.env.SPARROW_API_KEY&&(console['warn'](_0x4cf5dc(0x1f5)+_0x4cf5dc(0x18b)+_0x4cf5dc(0x211)+_0x4cf5dc(0x20f)),console['warn'](_0x4cf5dc(0x252)),console[_0x4cf5dc(0x1a0)]('\x20\x20\x20-\x20Windo'+_0x4cf5dc(0x1fa)+'PARROW_API'+_0x4cf5dc(0x1c4)+_0x4cf5dc(0x18c)),console[_0x4cf5dc(0x1a0)](_0x4cf5dc(0x1cc)+_0x4cf5dc(0x1ad)+_0x4cf5dc(0x21f)+_0x4cf5dc(0x239)+'your-key\x22\x0a'));try{const _0x292705=a1_0x5da455[_0x4cf5dc(0x23b)](__dirname,'../../..'),_0x5b9eea=a1_0x5da455['join'](_0x292705,'dist',_0x4cf5dc(0x198),_0x4cf5dc(0x201)),_0x4b09fc=await runTests(_0x4cf5dc(0x1d8),[_0x5b9eea]);saveTsv(_0x4b09fc,_0x3b7da1);const _0x478b2a=Object[_0x4cf5dc(0x1ed)](_0x4b09fc)[_0x4cf5dc(0x22c)](_0x34b9a2=>_0x34b9a2[_0x4cf5dc(0x21e)]==='통과')[_0x4cf5dc(0x1b9)],_0x46292e=Object['values'](_0x4b09fc)[_0x4cf5dc(0x22c)](_0x5181b5=>_0x5181b5['status']==='실패')[_0x4cf5dc(0x1b9)];console[_0x4cf5dc(0x1fb)]('\x0a━━━━━━━━━'+_0x4cf5dc(0x248)+'━━━━━━━━━━'+_0x4cf5dc(0x248)+'━━'),console[_0x4cf5dc(0x1fb)](_0x4cf5dc(0x21a)),console[_0x4cf5dc(0x1fb)]('\x20\x20\x20통과:\x20'+_0x478b2a+'/'+(_0x478b2a+_0x46292e)),console[_0x4cf5dc(0x1fb)]('\x20\x20\x20실패:\x20'+_0x46292e),console[_0x4cf5dc(0x1fb)](_0x4cf5dc(0x1b5)+_0x3b7da1),console[_0x4cf5dc(0x1fb)](_0x503f3a['TdsGu']),process[_0x4cf5dc(0x1e0)](_0x46292e>0x0?0x1:0x0);}catch(_0x38ee04){console[_0x4cf5dc(0x20e)](_0x4cf5dc(0x1b7)+'패:',_0x38ee04['message']),process[_0x4cf5dc(0x1e0)](0x1);}}main();
@@ -1,82 +1 @@
1
- import { config } from 'dotenv';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import { fileURLToPath } from 'url';
5
- import { logger } from '../utils/logger.js';
6
- // 환경 변수 로드: 실행 CWD가 달라도 루트 .env를 확실히 찾도록 처리
7
- const moduleDir = path.dirname(fileURLToPath(import.meta.url));
8
- const repoRootEnvPath = path.resolve(moduleDir, '../../.env');
9
- const cwdEnvPath = path.resolve(process.cwd(), '.env');
10
- // 존재 여부 기반 우선순위: ENV_PATH > CWD/.env > repoRoot/.env
11
- const explicitEnvPath = process.env.ENV_PATH ? path.resolve(process.cwd(), process.env.ENV_PATH) : undefined;
12
- const candidateEnvPaths = [explicitEnvPath, cwdEnvPath, repoRootEnvPath].filter(Boolean);
13
- const envPath = candidateEnvPaths.find(p => {
14
- try {
15
- return fs.existsSync(p);
16
- }
17
- catch {
18
- return false;
19
- }
20
- }) || repoRootEnvPath;
21
- config({ path: envPath });
22
- export const appConfig = {
23
- ollama: {
24
- baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434',
25
- model: process.env.OLLAMA_MODEL || 'gemma2:27b'
26
- },
27
- openai: process.env.OPENAI_API_KEY ? {
28
- apiKey: process.env.OPENAI_API_KEY,
29
- model: process.env.OPENAI_MODEL || 'gpt-4o'
30
- } : undefined,
31
- anthropic: process.env.ANTHROPIC_API_KEY ? {
32
- apiKey: process.env.ANTHROPIC_API_KEY,
33
- model: process.env.ANTHROPIC_MODEL || 'claude-3-5-sonnet-20240620'
34
- } : undefined,
35
- gemini: process.env.GEMINI_API_KEY ? {
36
- apiKey: process.env.GEMINI_API_KEY,
37
- model: process.env.GEMINI_MODEL || 'gemini-1.5-pro'
38
- } : undefined,
39
- aws: process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY ? {
40
- region: process.env.AWS_REGION || 'us-east-1',
41
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
42
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
43
- sessionToken: process.env.AWS_SESSION_TOKEN,
44
- model: process.env.AWS_MODEL || 'anthropic.claude-3-sonnet-20240229-v1:0'
45
- } : undefined,
46
- s3: {
47
- bucket: process.env.STORAGE_CONTAINER_NAME || '',
48
- region: process.env.STORAGE_REGION || 'us-east-1',
49
- // 개발 환경에서만 환경 변수 사용, 프로덕션에서는 암호화된 키 사용
50
- accessKeyId: process.env.STORAGE_ACCESS_ID || '',
51
- secretAccessKey: process.env.STORAGE_SECRET_KEY || '',
52
- // 선택: 커스텀 엔드포인트 (미설정 시 표준 엔드포인트 사용)
53
- endpoint: process.env.STORAGE_ENDPOINT
54
- },
55
- sparrow: {
56
- // 온디맨드 API 명세 반영
57
- baseUrl: process.env.SPARROW_API_URL || 'https://ondemand.sparrowcloud.ai',
58
- apiKey: process.env.SPARROW_API_KEY || ''
59
- },
60
- notion: process.env.NOTION_API_KEY ? {
61
- apiKey: process.env.NOTION_API_KEY,
62
- databaseId: process.env.NOTION_DATABASE_ID || ''
63
- } : undefined
64
- };
65
- // 필수 환경 변수 검증
66
- // 스토리지 키와 컨테이너 이름은 코드에 암호화되어 포함되므로 환경 변수는 선택적
67
- const requiredEnvVars = [
68
- 'SPARROW_API_KEY'
69
- ];
70
- // 개발 환경에서만 스토리지 키 환경 변수 검증 (프로덕션에서는 암호화된 키 사용)
71
- if (process.env.NODE_ENV === 'development') {
72
- if (!process.env.STORAGE_ACCESS_ID || !process.env.STORAGE_SECRET_KEY) {
73
- logger.warn('개발 환경: 스토리지 키 환경 변수가 설정되지 않았습니다. 암호화된 키를 사용합니다.');
74
- }
75
- }
76
- for (const envVar of requiredEnvVars) {
77
- if (!process.env[envVar]) {
78
- const triedPaths = [envPath].filter(Boolean).join(', ');
79
- logger.error(`필수 환경 변수가 설정되지 않았습니다: ${envVar} (로드 경로: ${triedPaths})`);
80
- throw new Error(`필수 환경 변수가 설정되지 않았습니다: ${envVar} (로드 경로: ${triedPaths})`);
81
- }
82
- }
1
+ const a2_0xdd3a3a=a2_0xfd9c;(function(_0x4fd07a,_0x3ed84a){const _0x161271=a2_0xfd9c,_0x4f40a=_0x4fd07a();while(!![]){try{const _0x4070c8=-parseInt(_0x161271(0x106))/0x1+-parseInt(_0x161271(0xf2))/0x2+parseInt(_0x161271(0x11d))/0x3*(parseInt(_0x161271(0xf1))/0x4)+parseInt(_0x161271(0x122))/0x5*(parseInt(_0x161271(0x125))/0x6)+-parseInt(_0x161271(0xeb))/0x7*(parseInt(_0x161271(0x113))/0x8)+parseInt(_0x161271(0x126))/0x9*(-parseInt(_0x161271(0xfd))/0xa)+parseInt(_0x161271(0xf8))/0xb;if(_0x4070c8===_0x3ed84a)break;else _0x4f40a['push'](_0x4f40a['shift']());}catch(_0xec24ce){_0x4f40a['push'](_0x4f40a['shift']());}}}(a2_0x3df2,0xa04ab));const a2_0x10d5a4=(function(){let _0x11e2a9=!![];return function(_0x3240a5,_0x3c565f){const _0x26b087=_0x11e2a9?function(){const _0x3ada83=a2_0xfd9c;if(_0x3c565f){const _0x220ad2=_0x3c565f[_0x3ada83(0x107)](_0x3240a5,arguments);return _0x3c565f=null,_0x220ad2;}}:function(){};return _0x11e2a9=![],_0x26b087;};}()),a2_0x25a82e=a2_0x10d5a4(this,function(){const _0x413a24=a2_0xfd9c;return a2_0x25a82e[_0x413a24(0x11f)]()['search'](_0x413a24(0xf9)+'+$')['toString']()[_0x413a24(0x100)+'r'](a2_0x25a82e)[_0x413a24(0xee)](_0x413a24(0xf9)+'+$');});function a2_0x3df2(){const _0x1d9822=['293347zozTnG','apply','지\x20키\x20환경\x20변수가','../../.env','240620','accessKeyI','claude-3-5','\x20(로드\x20경로:\x20','resolve','demand.spa','0229-v1:0','error','다.\x20암호화된\x20키를','112TVpHjC','region','필수\x20환경\x20변수가\x20','baseUrl','gemini','claude-3-s','SPARROW_AP','endpoint','bucket','anthropic.','49200cpwXkr','existsSync','toString','.env','path','222030LlOvHr','developmen','ollama','54OqbTJl','333lNeLcu','secretAcce','gemini-1.5','gemma2:27b','15673HTbKGq','alhost:114','-sonnet-20','search','apiKey','openai','268TDPnvr','955500gTlsgl','설정되지\x20않았습니다','sparrow','ssKey','-pro','filter','1536271LnBTXS','(((.+)+)+)','anthropic','gpt-4o','\x20설정되지\x20않았습니','48410hpCnwH','http://loc','https://on','constructo','us-east-1','cwd','onnet-2024','개발\x20환경:\x20스토리','\x20사용합니다.'];a2_0x3df2=function(){return _0x1d9822;};return a2_0x3df2();}a2_0x25a82e();import{config}from'dotenv';function a2_0xfd9c(_0x203882,_0x5b8415){_0x203882=_0x203882-0xeb;const _0x3e7ed8=a2_0x3df2();let _0x25a82e=_0x3e7ed8[_0x203882];return _0x25a82e;}import a2_0x253c64 from'path';import a2_0x4bee55 from'fs';import{fileURLToPath}from'url';import{logger}from'../utils/logger.js';const moduleDir=a2_0x253c64['dirname'](fileURLToPath(import.meta.url)),repoRootEnvPath=a2_0x253c64[a2_0xdd3a3a(0x10e)](moduleDir,a2_0xdd3a3a(0x109)),cwdEnvPath=a2_0x253c64[a2_0xdd3a3a(0x10e)](process[a2_0xdd3a3a(0x102)](),a2_0xdd3a3a(0x120)),explicitEnvPath=process.env.ENV_PATH?a2_0x253c64[a2_0xdd3a3a(0x10e)](process['cwd'](),process.env.ENV_PATH):undefined,candidateEnvPaths=[explicitEnvPath,cwdEnvPath,repoRootEnvPath][a2_0xdd3a3a(0xf7)](Boolean),envPath=candidateEnvPaths['find'](_0x121e9e=>{const _0x26b71b=a2_0xdd3a3a;try{return a2_0x4bee55[_0x26b71b(0x11e)](_0x121e9e);}catch{return![];}})||repoRootEnvPath,a2_0x40ac63={};a2_0x40ac63[a2_0xdd3a3a(0x121)]=envPath,config(a2_0x40ac63);const a2_0x3de765={};a2_0x3de765[a2_0xdd3a3a(0x116)]=process.env.OLLAMA_BASE_URL||a2_0xdd3a3a(0xfe)+a2_0xdd3a3a(0xec)+'34',a2_0x3de765['model']=process.env.OLLAMA_MODEL||a2_0xdd3a3a(0x129);const a2_0x424bfc={};a2_0x424bfc[a2_0xdd3a3a(0x124)]=a2_0x3de765,a2_0x424bfc[a2_0xdd3a3a(0xf0)]=process.env.OPENAI_API_KEY?{'apiKey':process.env.OPENAI_API_KEY,'model':process.env.OPENAI_MODEL||a2_0xdd3a3a(0xfb)}:undefined,a2_0x424bfc[a2_0xdd3a3a(0xfa)]=process.env.ANTHROPIC_API_KEY?{'apiKey':process.env.ANTHROPIC_API_KEY,'model':process.env.ANTHROPIC_MODEL||a2_0xdd3a3a(0x10c)+a2_0xdd3a3a(0xed)+a2_0xdd3a3a(0x10a)}:undefined,a2_0x424bfc[a2_0xdd3a3a(0x117)]=process.env.GEMINI_API_KEY?{'apiKey':process.env.GEMINI_API_KEY,'model':process.env.GEMINI_MODEL||a2_0xdd3a3a(0x128)+a2_0xdd3a3a(0xf6)}:undefined,a2_0x424bfc['aws']=process.env.AWS_ACCESS_KEY_ID&&process.env.AWS_SECRET_ACCESS_KEY?{'region':process.env.AWS_REGION||a2_0xdd3a3a(0x101),'accessKeyId':process.env.AWS_ACCESS_KEY_ID,'secretAccessKey':process.env.AWS_SECRET_ACCESS_KEY,'sessionToken':process.env.AWS_SESSION_TOKEN,'model':process.env.AWS_MODEL||a2_0xdd3a3a(0x11c)+a2_0xdd3a3a(0x118)+a2_0xdd3a3a(0x103)+a2_0xdd3a3a(0x110)}:undefined,a2_0x424bfc['s3']={},a2_0x424bfc[a2_0xdd3a3a(0xf4)]={},a2_0x424bfc['notion']=process.env.NOTION_API_KEY?{'apiKey':process.env.NOTION_API_KEY,'databaseId':process.env.NOTION_DATABASE_ID||''}:undefined,a2_0x424bfc['s3'][a2_0xdd3a3a(0x11b)]=process.env.STORAGE_CONTAINER_NAME||'',a2_0x424bfc['s3'][a2_0xdd3a3a(0x114)]=process.env.STORAGE_REGION||a2_0xdd3a3a(0x101),a2_0x424bfc['s3'][a2_0xdd3a3a(0x10b)+'d']=process.env.STORAGE_ACCESS_ID||'',a2_0x424bfc['s3'][a2_0xdd3a3a(0x127)+a2_0xdd3a3a(0xf5)]=process.env.STORAGE_SECRET_KEY||'',a2_0x424bfc['s3'][a2_0xdd3a3a(0x11a)]=process.env.STORAGE_ENDPOINT,a2_0x424bfc[a2_0xdd3a3a(0xf4)][a2_0xdd3a3a(0x116)]=process.env.SPARROW_API_URL||a2_0xdd3a3a(0xff)+a2_0xdd3a3a(0x10f)+'rrowcloud.'+'ai',a2_0x424bfc[a2_0xdd3a3a(0xf4)][a2_0xdd3a3a(0xef)]=process.env.SPARROW_API_KEY||'';export const appConfig=a2_0x424bfc;const requiredEnvVars=[a2_0xdd3a3a(0x119)+'I_KEY'];process.env.NODE_ENV===a2_0xdd3a3a(0x123)+'t'&&((!process.env.STORAGE_ACCESS_ID||!process.env.STORAGE_SECRET_KEY)&&logger['warn'](a2_0xdd3a3a(0x104)+a2_0xdd3a3a(0x108)+a2_0xdd3a3a(0xfc)+a2_0xdd3a3a(0x112)+a2_0xdd3a3a(0x105)));for(const envVar of requiredEnvVars){if(!process.env[envVar]){const triedPaths=[envPath][a2_0xdd3a3a(0xf7)](Boolean)['join'](',\x20');logger[a2_0xdd3a3a(0x111)]('필수\x20환경\x20변수가\x20'+a2_0xdd3a3a(0xf3)+':\x20'+envVar+a2_0xdd3a3a(0x10d)+triedPaths+')');throw new Error(a2_0xdd3a3a(0x115)+a2_0xdd3a3a(0xf3)+':\x20'+envVar+a2_0xdd3a3a(0x10d)+triedPaths+')');}}
@@ -1,26 +1 @@
1
- export const SPARROW_RULES = `# Sparrow MCP 규칙
2
-
3
- 당신은 최고의 보안 파트너로서 \`sparrow-mcp\`를 사용합니다. 다음 원칙을 따르십시오:
4
-
5
- ## 1. 전술적 도구 선택
6
- - **소스코드 진단 (SAST)**: 코드 자체의 취약점(SQLi, XSS 등)을 찾을 때 \`analyze_file_sast\`, \`analyze_files_sast\`, \`analyze_folder_sast\` 등을 사용합니다.
7
- - **오픈소스/라이브러리 진단 (SCA)**: \`package.json\`, \`pom.xml\` 등 의존성 파일을 분석하여 취약한 라이브러리를 찾을 때 \`analyze_file_sca\`, \`analyze_folder_sca\` 등을 사용합니다.
8
- - **파일 하나도 놓치지 마세요**: 분석 범위에 따라 적절한 도구(파일/폴더/ZIP)를 선택하세요.
9
-
10
- ## 2. 분석-조치 루프 (The Loop)
11
- 1. **Start**: 분석을 시작하고 \`analysisId\`를 받습니다.
12
- 2. **Track**: \`track_analysis_progress\`로 끈기 있게 기다립니다. (사용자를 불안하게 하지 마세요)
13
- 3. **Fetch**: 분석이 완료되면 결과 유형에 따라 \`get_analysis_results\` (SAST) 또는 \`get_sca_analysis_results\` (SCA)로 상세 리포트를 가져옵니다.
14
- 4. **Action**: SAST 취약점이 있다면 **반드시** \`generate_secure_code\`로 해결책을 제시하고, SCA 취약점은 리포트의 가이드에 따라 라이브러리 업데이트를 제안합니다.
15
-
16
- ## 3. 사용자 커뮤니케이션
17
- - "취약점이 발견되었습니다"보다 **"심각한 이슈(High)를 막았습니다"**라고 긍정적인 가치를 전달하세요.
18
- - 수정 제안 시, 원본과 안전한 코드의 차이(Diff)를 명확히 보여주어 신뢰를 얻으세요.`;
19
- export const ANALYSIS_STAGES = {
20
- 'INIT': { id: 'INIT', name: 'Initialization', description: 'Waiting for analysis to start.', progress: null, order: 1 },
21
- 'READY': { id: 'READY', name: 'Ready', description: 'Ready to start analysis.', progress: null, order: 2 },
22
- 'PRE_PROCESS': { id: 'PRE_PROCESS', description: 'Pre-processing...', name: 'Pre-processing', progress: null, order: 3 },
23
- 'ANALYSIS': { id: 'ANALYSIS', description: 'Analysis in progress...', name: 'Analysis', progress: 0, order: 4 },
24
- 'POST_PROCESS': { id: 'POST_PROCESS', description: 'Post-processing...', name: 'Post-processing', progress: null, order: 5 },
25
- 'COMPLETE': { id: 'COMPLETE', description: 'Analysis completed successfully.', name: 'Complete', progress: 100, order: 6 }
26
- };
1
+ const a3_0x11432c=a3_0x505d;(function(_0x48f084,_0xce78a6){const _0x27b7e5=a3_0x505d,_0x4ebf3c=_0x48f084();while(!![]){try{const _0x2274e8=parseInt(_0x27b7e5(0x12b))/0x1+-parseInt(_0x27b7e5(0x105))/0x2*(-parseInt(_0x27b7e5(0x132))/0x3)+-parseInt(_0x27b7e5(0xce))/0x4+-parseInt(_0x27b7e5(0x103))/0x5*(-parseInt(_0x27b7e5(0x106))/0x6)+parseInt(_0x27b7e5(0x124))/0x7*(-parseInt(_0x27b7e5(0x135))/0x8)+parseInt(_0x27b7e5(0x130))/0x9*(parseInt(_0x27b7e5(0x125))/0xa)+parseInt(_0x27b7e5(0xcc))/0xb*(parseInt(_0x27b7e5(0x11f))/0xc);if(_0x2274e8===_0xce78a6)break;else _0x4ebf3c['push'](_0x4ebf3c['shift']());}catch(_0x397855){_0x4ebf3c['push'](_0x4ebf3c['shift']());}}}(a3_0x7c77,0x404ec));const a3_0x44c749=(function(){let _0x417767=!![];return function(_0x394d8f,_0x1661bd){const _0x16cbc7=_0x417767?function(){const _0x5cd91f=a3_0x505d;if(_0x1661bd){const _0x4b4ad1=_0x1661bd[_0x5cd91f(0xdd)](_0x394d8f,arguments);return _0x1661bd=null,_0x4b4ad1;}}:function(){};return _0x417767=![],_0x16cbc7;};}()),a3_0x88c750=a3_0x44c749(this,function(){const _0x2f6c68=a3_0x505d,_0x262295={};_0x262295['JYRsG']='(((.+)+)+)'+'+$';const _0x1c2274=_0x262295;return a3_0x88c750[_0x2f6c68(0xcd)]()['search'](_0x1c2274[_0x2f6c68(0x112)])[_0x2f6c68(0xcd)]()[_0x2f6c68(0x100)+'r'](a3_0x88c750)[_0x2f6c68(0xc8)](_0x2f6c68(0xf7)+'+$');});a3_0x88c750();export const SPARROW_RULES=a3_0x11432c(0x123)+a3_0x11432c(0xc6)+a3_0x11432c(0x108)+a3_0x11432c(0x10a)+a3_0x11432c(0x136)+a3_0x11432c(0xdf)+'원칙을\x20따르십시오:'+a3_0x11432c(0x115)+a3_0x11432c(0xd5)+a3_0x11432c(0xc7)+a3_0x11432c(0xfb)+a3_0x11432c(0xbd)+a3_0x11432c(0xf2)+a3_0x11432c(0xfa)+a3_0x11432c(0xe8)+a3_0x11432c(0x102)+a3_0x11432c(0xc5)+a3_0x11432c(0xec)+a3_0x11432c(0x109)+a3_0x11432c(0xf6)+'ast`\x20등을\x20사용'+a3_0x11432c(0x10c)+'픈소스/라이브러리\x20'+a3_0x11432c(0xef)+a3_0x11432c(0xe2)+'.json`,\x20`p'+a3_0x11432c(0x113)+a3_0x11432c(0x10d)+a3_0x11432c(0xf0)+'러리를\x20찾을\x20때\x20`'+a3_0x11432c(0x116)+a3_0x11432c(0x121)+a3_0x11432c(0xe0)+a3_0x11432c(0x133)+a3_0x11432c(0xd1)+a3_0x11432c(0x118)+a3_0x11432c(0x11d)+a3_0x11432c(0xc4)+a3_0x11432c(0x110)+a3_0x11432c(0x117)+a3_0x11432c(0x12f)+'#\x202.\x20분석-조치'+a3_0x11432c(0x11b)+'oop)\x0a1.\x20**'+a3_0x11432c(0x111)+a3_0x11432c(0xe1)+a3_0x11432c(0x10f)+'를\x20받습니다.\x0a2.'+a3_0x11432c(0xe3)+a3_0x11432c(0xd8)+a3_0x11432c(0x11c)+a3_0x11432c(0xd6)+a3_0x11432c(0x12a)+a3_0x11432c(0xbf)+a3_0x11432c(0x120)+a3_0x11432c(0xdc)+a3_0x11432c(0xe7)+'료되면\x20결과\x20유형에'+a3_0x11432c(0xf8)+a3_0x11432c(0xbe)+a3_0x11432c(0xf5)+a3_0x11432c(0xf1)+a3_0x11432c(0xdb)+a3_0x11432c(0x12c)+a3_0x11432c(0x134)+'\x20상세\x20리포트를\x20가'+a3_0x11432c(0xe9)+a3_0x11432c(0xfd)+a3_0x11432c(0xf4)+a3_0x11432c(0x129)+a3_0x11432c(0x11a)+a3_0x11432c(0xcf)+a3_0x11432c(0xed)+a3_0x11432c(0xfc)+a3_0x11432c(0xc0)+'의\x20가이드에\x20따라\x20'+a3_0x11432c(0x128)+a3_0x11432c(0xda)+a3_0x11432c(0x10e)+'커뮤니케이션\x0a-\x20\x22'+a3_0x11432c(0xcb)+a3_0x11432c(0xd2)+'각한\x20이슈(High'+a3_0x11432c(0x10b)+a3_0x11432c(0x107)+a3_0x11432c(0xd0)+'-\x20수정\x20제안\x20시,'+'\x20원본과\x20안전한\x20코'+'드의\x20차이(Diff'+a3_0x11432c(0x127)+a3_0x11432c(0xc1)+'.';const a3_0x4d9b5d={};a3_0x4d9b5d['id']=a3_0x11432c(0xca),a3_0x4d9b5d[a3_0x11432c(0xe5)]=a3_0x11432c(0xeb)+a3_0x11432c(0x114),a3_0x4d9b5d['descriptio'+'n']=a3_0x11432c(0x12e)+a3_0x11432c(0x126)+a3_0x11432c(0xe6),a3_0x4d9b5d[a3_0x11432c(0xde)]=null,a3_0x4d9b5d[a3_0x11432c(0x11e)]=0x1;const a3_0xb0e60f={};a3_0xb0e60f['id']=a3_0x11432c(0xf3),a3_0xb0e60f[a3_0x11432c(0xe5)]=a3_0x11432c(0x101),a3_0xb0e60f[a3_0x11432c(0xe4)+'n']=a3_0x11432c(0xc2)+a3_0x11432c(0xfe)+a3_0x11432c(0x119),a3_0xb0e60f['progress']=null,a3_0xb0e60f[a3_0x11432c(0x11e)]=0x2;function a3_0x505d(_0x1fee4a,_0x8ea2a3){_0x1fee4a=_0x1fee4a-0xbd;const _0xe4c03b=a3_0x7c77();let _0x88c750=_0xe4c03b[_0x1fee4a];return _0x88c750;}const a3_0x5dfb2a={};a3_0x5dfb2a['id']='PRE_PROCES'+'S',a3_0x5dfb2a[a3_0x11432c(0xe4)+'n']=a3_0x11432c(0x122)+'sing...',a3_0x5dfb2a[a3_0x11432c(0xe5)]='Pre-proces'+a3_0x11432c(0xd4),a3_0x5dfb2a[a3_0x11432c(0xde)]=null,a3_0x5dfb2a['order']=0x3;const a3_0x186d82={};a3_0x186d82['id']=a3_0x11432c(0xea),a3_0x186d82['descriptio'+'n']=a3_0x11432c(0xd3)+a3_0x11432c(0xc9)+'...',a3_0x186d82[a3_0x11432c(0xe5)]=a3_0x11432c(0xc3),a3_0x186d82[a3_0x11432c(0xde)]=0x0,a3_0x186d82[a3_0x11432c(0x11e)]=0x4;const a3_0x34b24b={};function a3_0x7c77(){const _0x37ae5d=['lder_sca`\x20','ts`\x20(SCA)로','34288DEnQhm','row-mcp`를\x20','코드\x20자체의\x20취약점','nalysis_re','.\x20(사용자를\x20불안','A\x20취약점은\x20리포트','어\x20신뢰를\x20얻으세요','Ready\x20to\x20s','Analysis',':\x20분석\x20범위에\x20따','\x20`analyze_','MCP\x20규칙\x0a\x0a당신','**소스코드\x20진단\x20','search','n\x20progress','INIT','취약점이\x20발견되었습','3498fzpOBD','toString','1374656AjbXoF','te_secure_','치를\x20전달하세요.\x0a','등을\x20사용합니다.\x0a','니다\x22보다\x20**\x22심','Analysis\x20i','sing','적\x20도구\x20선택\x0a-\x20','ogress`로\x20끈','Post-proce',':\x20`track_a','PRE_PROCES','를\x20제안합니다.\x0a\x0a','t_sca_anal','\x0a3.\x20**Fetc','apply','progress','사용합니다.\x20다음\x20','analyze_fo','석을\x20시작하고\x20`a',':\x20`package','\x20**Track**','descriptio','name','\x20to\x20start.','h**:\x20분석이\x20완','`analyze_f','져옵니다.\x0a4.\x20*','ANALYSIS','Initializa','files_sast','code`로\x20해결책','POST_PROCE','진단\x20(SCA)**','하여\x20취약한\x20라이브','ST)\x20또는\x20`ge','(SQLi,\x20XSS','READY','\x20SAST\x20취약점이','sults`\x20(SA','e_folder_s','(((.+)+)+)','\x20따라\x20`get_a','ssing','\x20등)을\x20찾을\x20때\x20','(SAST)**:\x20','을\x20제시하고,\x20SC','*Action**:','tart\x20analy','Analysis\x20c','constructo','Ready','ile_sast`,','215AfoxXR','Complete','142102CqWBIU','15978OZPnYB','*라고\x20긍정적인\x20가','은\x20최고의\x20보안\x20파','`,\x20`analyz','트너로서\x20`spar',')를\x20막았습니다\x22*','합니다.\x0a-\x20**오','의존성\x20파일을\x20분석','##\x203.\x20사용자\x20','nalysisId`','라\x20적절한\x20도구(파','Start**:\x20분','JYRsG','om.xml`\x20등\x20','tion','\x0a\x0a##\x201.\x20전술','analyze_fi','일/폴더/ZIP)를','-\x20**파일\x20하나도','sis.','**\x20`genera','\x20루프\x20(The\x20L','nalysis_pr','\x20놓치지\x20마세요**','order','2040XdwnFp','하게\x20하지\x20마세요)','le_sca`,\x20`','Pre-proces','#\x20Sparrow\x20','301xZIcZY','350RUoatt','r\x20analysis',')를\x20명확히\x20보여주','라이브러리\x20업데이트','\x20있다면\x20**반드시','기\x20있게\x20기다립니다','104200gEweSn','ysis_resul','COMPLETE','Waiting\x20fo','\x20선택하세요.\x0a\x0a#','96813juRcWM','ompleted\x20s','6barYxY'];a3_0x7c77=function(){return _0x37ae5d;};return a3_0x7c77();}a3_0x34b24b['id']=a3_0x11432c(0xee)+'SS',a3_0x34b24b[a3_0x11432c(0xe4)+'n']='Post-proce'+'ssing...',a3_0x34b24b[a3_0x11432c(0xe5)]=a3_0x11432c(0xd7)+a3_0x11432c(0xf9),a3_0x34b24b[a3_0x11432c(0xde)]=null,a3_0x34b24b[a3_0x11432c(0x11e)]=0x5;const a3_0x231092={};a3_0x231092['id']='COMPLETE',a3_0x231092[a3_0x11432c(0xe4)+'n']=a3_0x11432c(0xff)+a3_0x11432c(0x131)+'uccessfull'+'y.',a3_0x231092[a3_0x11432c(0xe5)]=a3_0x11432c(0x104),a3_0x231092[a3_0x11432c(0xde)]=0x64,a3_0x231092[a3_0x11432c(0x11e)]=0x6;const a3_0x508678={};a3_0x508678[a3_0x11432c(0xca)]=a3_0x4d9b5d,a3_0x508678['READY']=a3_0xb0e60f,a3_0x508678[a3_0x11432c(0xd9)+'S']=a3_0x5dfb2a,a3_0x508678['ANALYSIS']=a3_0x186d82,a3_0x508678[a3_0x11432c(0xee)+'SS']=a3_0x34b24b,a3_0x508678[a3_0x11432c(0x12d)]=a3_0x231092;export const ANALYSIS_STAGES=a3_0x508678;