agent-security-scanner-mcp 2.0.5 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -1
- package/index.js +279 -10
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -65,6 +65,22 @@ The scanner works without tree-sitter using regex-based detection, but AST analy
|
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
68
|
+
## What's New in v2.0.7
|
|
69
|
+
|
|
70
|
+
- **SARIF output format** - `scan_security` now supports `output_format: 'sarif'` for GitHub/GitLab Security tab integration
|
|
71
|
+
- **GitHub Code Scanning** - Upload results directly to GitHub Advanced Security
|
|
72
|
+
- **GitLab SAST** - Compatible with GitLab's security dashboard
|
|
73
|
+
- **Full SARIF 2.1.0 compliance** - Includes rules, locations, fix suggestions, CWE/OWASP metadata
|
|
74
|
+
|
|
75
|
+
## What's New in v2.0.6
|
|
76
|
+
|
|
77
|
+
- **fix_security reliability overhaul** - Fixes now validated before applying to prevent malformed code output
|
|
78
|
+
- **Python f-string SQL injection** - Now detects AND fixes `f"SELECT...{var}"` patterns
|
|
79
|
+
- **Python .format() SQL injection** - Now fixes `"SELECT...{}".format(var)` patterns
|
|
80
|
+
- **JavaScript template literal SQL injection** - Now fixes `` `SELECT...${var}` `` patterns
|
|
81
|
+
- **Multi-pattern fix engine** - Each vulnerability type can have multiple language-specific fix patterns
|
|
82
|
+
- **Syntax validation** - Rejects fixes with unbalanced quotes, brackets, or obvious syntax errors
|
|
83
|
+
|
|
68
84
|
## What's New in v2.0.5
|
|
69
85
|
|
|
70
86
|
- **Claude Code per-project fix** - `init claude-code` now uses `claude mcp add` CLI for reliable per-project configuration
|
|
@@ -359,6 +375,7 @@ Scan a file for security vulnerabilities and return issues with suggested fixes.
|
|
|
359
375
|
```
|
|
360
376
|
Parameters:
|
|
361
377
|
file_path (string): Absolute path to the file to scan
|
|
378
|
+
output_format (string, optional): 'json' (default) or 'sarif' for GitHub/GitLab integration
|
|
362
379
|
|
|
363
380
|
Returns:
|
|
364
381
|
- List of security issues
|
|
@@ -368,7 +385,7 @@ Returns:
|
|
|
368
385
|
- Suggested fixes
|
|
369
386
|
```
|
|
370
387
|
|
|
371
|
-
**Example output:**
|
|
388
|
+
**Example output (JSON - default):**
|
|
372
389
|
```json
|
|
373
390
|
{
|
|
374
391
|
"file": "/path/to/file.js",
|
|
@@ -394,6 +411,36 @@ Returns:
|
|
|
394
411
|
}
|
|
395
412
|
```
|
|
396
413
|
|
|
414
|
+
**Example output (SARIF - for GitHub/GitLab):**
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
418
|
+
"version": "2.1.0",
|
|
419
|
+
"runs": [{
|
|
420
|
+
"tool": {
|
|
421
|
+
"driver": {
|
|
422
|
+
"name": "agent-security-scanner-mcp",
|
|
423
|
+
"version": "2.0.7",
|
|
424
|
+
"rules": [...]
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
"results": [
|
|
428
|
+
{
|
|
429
|
+
"ruleId": "sql-injection",
|
|
430
|
+
"level": "error",
|
|
431
|
+
"message": { "text": "SQL Injection detected" },
|
|
432
|
+
"locations": [{
|
|
433
|
+
"physicalLocation": {
|
|
434
|
+
"artifactLocation": { "uri": "file.js" },
|
|
435
|
+
"region": { "startLine": 15 }
|
|
436
|
+
}
|
|
437
|
+
}]
|
|
438
|
+
}
|
|
439
|
+
]
|
|
440
|
+
}]
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
397
444
|
### `fix_security`
|
|
398
445
|
|
|
399
446
|
Automatically fix all security issues in a file.
|
|
@@ -631,6 +678,64 @@ Package lists are sourced from [garak-llm](https://huggingface.co/garak-llm) Hug
|
|
|
631
678
|
|
|
632
679
|
---
|
|
633
680
|
|
|
681
|
+
## CI/CD Integration (SARIF)
|
|
682
|
+
|
|
683
|
+
Upload scan results to GitHub Security tab or GitLab Security Dashboard using SARIF format.
|
|
684
|
+
|
|
685
|
+
### GitHub Actions Example
|
|
686
|
+
|
|
687
|
+
```yaml
|
|
688
|
+
name: Security Scan
|
|
689
|
+
on: [push, pull_request]
|
|
690
|
+
|
|
691
|
+
jobs:
|
|
692
|
+
security:
|
|
693
|
+
runs-on: ubuntu-latest
|
|
694
|
+
steps:
|
|
695
|
+
- uses: actions/checkout@v4
|
|
696
|
+
|
|
697
|
+
- name: Setup Node.js
|
|
698
|
+
uses: actions/setup-node@v4
|
|
699
|
+
with:
|
|
700
|
+
node-version: '20'
|
|
701
|
+
|
|
702
|
+
- name: Run Security Scanner
|
|
703
|
+
run: |
|
|
704
|
+
npx agent-security-scanner-mcp scan src/ --format sarif --output results.sarif
|
|
705
|
+
|
|
706
|
+
- name: Upload SARIF to GitHub
|
|
707
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
708
|
+
with:
|
|
709
|
+
sarif_file: results.sarif
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### GitLab CI Example
|
|
713
|
+
|
|
714
|
+
```yaml
|
|
715
|
+
security_scan:
|
|
716
|
+
stage: test
|
|
717
|
+
script:
|
|
718
|
+
- npx agent-security-scanner-mcp scan src/ --format sarif --output gl-sast-report.json
|
|
719
|
+
artifacts:
|
|
720
|
+
reports:
|
|
721
|
+
sast: gl-sast-report.json
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### Programmatic Usage
|
|
725
|
+
|
|
726
|
+
```javascript
|
|
727
|
+
// Use output_format: 'sarif' parameter
|
|
728
|
+
const result = await client.callTool({
|
|
729
|
+
name: 'scan_security',
|
|
730
|
+
arguments: {
|
|
731
|
+
file_path: '/path/to/file.js',
|
|
732
|
+
output_format: 'sarif' // Returns SARIF 2.1.0 format
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
634
739
|
## Security Rules (359 total)
|
|
635
740
|
|
|
636
741
|
### By Language
|
package/index.js
CHANGED
|
@@ -28,7 +28,73 @@ const FIX_TEMPLATES = {
|
|
|
28
28
|
// ===========================================
|
|
29
29
|
"sql-injection": {
|
|
30
30
|
description: "Use parameterized queries instead of string concatenation",
|
|
31
|
-
|
|
31
|
+
patterns: [
|
|
32
|
+
// Python f-strings: cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
|
|
33
|
+
{
|
|
34
|
+
match: /f["'].*(?:SELECT|INSERT|UPDATE|DELETE).*\{(\w+)\}.*["']/i,
|
|
35
|
+
fix: (line) => {
|
|
36
|
+
// Extract the query and variable
|
|
37
|
+
const match = line.match(/(\w+\.(?:execute|query|run))\s*\(\s*f(["'])(.*?)(?:SELECT|INSERT|UPDATE|DELETE)(.*?)\{(\w+)\}(.*?)\2/i);
|
|
38
|
+
if (match) {
|
|
39
|
+
const [, method, quote, prefix, queryStart, varName, suffix] = match;
|
|
40
|
+
// Reconstruct as parameterized query
|
|
41
|
+
const cleanPrefix = prefix.replace(/\{[^}]+\}/g, '?');
|
|
42
|
+
const cleanSuffix = suffix.replace(/\{[^}]+\}/g, '?');
|
|
43
|
+
return line.replace(
|
|
44
|
+
/f(["']).*\1/,
|
|
45
|
+
`"${cleanPrefix}${queryStart.trim().toUpperCase()}${cleanSuffix}?", (${varName},)`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
// Simpler fallback for f-strings
|
|
49
|
+
return line.replace(/f(["'])(.*?)\{(\w+)\}(.*?)\1/, '"$2?$4", ($3,)');
|
|
50
|
+
},
|
|
51
|
+
languages: ['python']
|
|
52
|
+
},
|
|
53
|
+
// Python .format(): "SELECT ... WHERE id = {}".format(user_id)
|
|
54
|
+
{
|
|
55
|
+
match: /["'].*(?:SELECT|INSERT|UPDATE|DELETE).*\{\}.*["']\.format\s*\(/i,
|
|
56
|
+
fix: (line) => {
|
|
57
|
+
return line.replace(
|
|
58
|
+
/(["'])(.*?)\{\}(.*?)\1\.format\s*\(\s*(\w+)\s*\)/,
|
|
59
|
+
'"$2?$3", [$4]'
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
languages: ['python']
|
|
63
|
+
},
|
|
64
|
+
// Python % formatting: "SELECT ... WHERE id = %s" % user_id
|
|
65
|
+
{
|
|
66
|
+
match: /["'].*(?:SELECT|INSERT|UPDATE|DELETE).*%s.*["']\s*%\s*\(/i,
|
|
67
|
+
fix: (line) => {
|
|
68
|
+
return line.replace(
|
|
69
|
+
/(["'])(.*?)%s(.*?)\1\s*%\s*\(\s*(\w+)\s*,?\s*\)/,
|
|
70
|
+
'"$2?$3", [$4]'
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
languages: ['python']
|
|
74
|
+
},
|
|
75
|
+
// JS template literals: `SELECT * FROM users WHERE id = ${userId}`
|
|
76
|
+
{
|
|
77
|
+
match: /`.*(?:SELECT|INSERT|UPDATE|DELETE).*\$\{.*\}.*`/i,
|
|
78
|
+
fix: (line) => {
|
|
79
|
+
return line.replace(
|
|
80
|
+
/`(.*?)\$\{(\w+)\}(.*?)`/,
|
|
81
|
+
'"$1?$3", [$2]'
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
languages: ['javascript', 'typescript']
|
|
85
|
+
},
|
|
86
|
+
// Simple concatenation (no quotes inside): "SELECT ... WHERE id = " + userId
|
|
87
|
+
{
|
|
88
|
+
match: /["'](?:SELECT|INSERT|UPDATE|DELETE)[^"']+["']\s*\+\s*\w+(?!\s*\+\s*["'])/i,
|
|
89
|
+
fix: (line) => {
|
|
90
|
+
return line.replace(
|
|
91
|
+
/(["'])((?:SELECT|INSERT|UPDATE|DELETE)[^"']+)\1\s*\+\s*(\w+)/i,
|
|
92
|
+
'"$2?", [$3]'
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
languages: ['javascript', 'python', 'java', 'go', 'ruby', 'php']
|
|
96
|
+
}
|
|
97
|
+
]
|
|
32
98
|
},
|
|
33
99
|
"nosql-injection": {
|
|
34
100
|
description: "Sanitize MongoDB query inputs",
|
|
@@ -765,17 +831,96 @@ function runAnalyzer(filePath) {
|
|
|
765
831
|
}
|
|
766
832
|
}
|
|
767
833
|
|
|
834
|
+
// Validate that a fix produces valid syntax
|
|
835
|
+
function validateFix(original, fixed, language) {
|
|
836
|
+
// Rule 1: Fix must be different from original
|
|
837
|
+
if (fixed === original || !fixed) {
|
|
838
|
+
return { valid: false, reason: 'no_change' };
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Rule 2: Balanced quotes (ignore escaped quotes)
|
|
842
|
+
const unescaped = fixed.replace(/\\["'`]/g, '');
|
|
843
|
+
const singleQuotes = (unescaped.match(/'/g) || []).length;
|
|
844
|
+
const doubleQuotes = (unescaped.match(/"/g) || []).length;
|
|
845
|
+
const backticks = (unescaped.match(/`/g) || []).length;
|
|
846
|
+
if (singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0 || backticks % 2 !== 0) {
|
|
847
|
+
return { valid: false, reason: 'unbalanced_quotes' };
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Rule 3: Balanced brackets
|
|
851
|
+
const brackets = { '(': 0, '[': 0, '{': 0 };
|
|
852
|
+
const closers = { ')': '(', ']': '[', '}': '{' };
|
|
853
|
+
for (const char of unescaped) {
|
|
854
|
+
if (brackets[char] !== undefined) brackets[char]++;
|
|
855
|
+
if (closers[char]) brackets[closers[char]]--;
|
|
856
|
+
}
|
|
857
|
+
if (Object.values(brackets).some(v => v !== 0)) {
|
|
858
|
+
return { valid: false, reason: 'unbalanced_brackets' };
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Rule 4: No obvious syntax errors
|
|
862
|
+
const badPatterns = [
|
|
863
|
+
/""[^,\s\]);}]/, // empty string followed by unexpected char
|
|
864
|
+
/\+\s*[)\]}]/, // + followed by closing bracket
|
|
865
|
+
/,\s*\+/, // comma followed by +
|
|
866
|
+
/\(\s*\+/, // open paren followed by +
|
|
867
|
+
];
|
|
868
|
+
for (const pattern of badPatterns) {
|
|
869
|
+
if (pattern.test(fixed)) {
|
|
870
|
+
return { valid: false, reason: 'syntax_error' };
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return { valid: true };
|
|
875
|
+
}
|
|
876
|
+
|
|
768
877
|
// Generate fix suggestion for an issue
|
|
769
878
|
function generateFix(issue, line, language) {
|
|
770
879
|
const ruleId = issue.ruleId.toLowerCase();
|
|
771
880
|
|
|
772
|
-
for (const [
|
|
773
|
-
if (ruleId.includes(
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
881
|
+
for (const [templateId, template] of Object.entries(FIX_TEMPLATES)) {
|
|
882
|
+
if (!ruleId.includes(templateId)) continue;
|
|
883
|
+
|
|
884
|
+
// New: handle patterns array
|
|
885
|
+
if (template.patterns && Array.isArray(template.patterns)) {
|
|
886
|
+
for (const pattern of template.patterns) {
|
|
887
|
+
// Skip if language doesn't match
|
|
888
|
+
if (pattern.languages && !pattern.languages.includes(language)) {
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// Skip if pattern doesn't match the line
|
|
893
|
+
if (!pattern.match.test(line)) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// Try the fix
|
|
898
|
+
const candidate = pattern.fix(line, language);
|
|
899
|
+
const validation = validateFix(line, candidate, language);
|
|
900
|
+
|
|
901
|
+
if (validation.valid) {
|
|
902
|
+
return {
|
|
903
|
+
description: template.description,
|
|
904
|
+
original: line,
|
|
905
|
+
fixed: candidate
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
// If invalid, try next pattern
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Fallback: old-style single fix function (backward compatible)
|
|
913
|
+
if (template.fix && typeof template.fix === 'function') {
|
|
914
|
+
const candidate = template.fix(line, language);
|
|
915
|
+
const validation = validateFix(line, candidate, language);
|
|
916
|
+
|
|
917
|
+
if (validation.valid) {
|
|
918
|
+
return {
|
|
919
|
+
description: template.description,
|
|
920
|
+
original: line,
|
|
921
|
+
fixed: candidate
|
|
922
|
+
};
|
|
923
|
+
}
|
|
779
924
|
}
|
|
780
925
|
}
|
|
781
926
|
|
|
@@ -804,14 +949,126 @@ export function createSandboxServer() {
|
|
|
804
949
|
return server;
|
|
805
950
|
}
|
|
806
951
|
|
|
952
|
+
// SARIF (Static Analysis Results Interchange Format) conversion
|
|
953
|
+
// Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
954
|
+
function convertToSarif(filePath, language, issues) {
|
|
955
|
+
const severityToLevel = {
|
|
956
|
+
'ERROR': 'error',
|
|
957
|
+
'WARNING': 'warning',
|
|
958
|
+
'INFO': 'note',
|
|
959
|
+
'HINT': 'note'
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
// Build rules from unique rule IDs
|
|
963
|
+
const rulesMap = new Map();
|
|
964
|
+
issues.forEach(issue => {
|
|
965
|
+
if (!rulesMap.has(issue.ruleId)) {
|
|
966
|
+
rulesMap.set(issue.ruleId, {
|
|
967
|
+
id: issue.ruleId,
|
|
968
|
+
name: issue.ruleId.split('.').pop().replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
|
|
969
|
+
shortDescription: {
|
|
970
|
+
text: issue.message.replace(/^\[.*?\]\s*/, '') // Remove [RuleName] prefix
|
|
971
|
+
},
|
|
972
|
+
defaultConfiguration: {
|
|
973
|
+
level: severityToLevel[issue.severity] || 'warning'
|
|
974
|
+
},
|
|
975
|
+
properties: {
|
|
976
|
+
tags: ['security'],
|
|
977
|
+
...(issue.metadata?.cwe && { 'security-severity': '7.0' }),
|
|
978
|
+
},
|
|
979
|
+
helpUri: issue.metadata?.references?.[0] || `https://cwe.mitre.org/data/definitions/${issue.metadata?.cwe?.replace('CWE-', '')}.html`
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
// Build results
|
|
985
|
+
const results = issues.map(issue => ({
|
|
986
|
+
ruleId: issue.ruleId,
|
|
987
|
+
level: severityToLevel[issue.severity] || 'warning',
|
|
988
|
+
message: {
|
|
989
|
+
text: issue.message
|
|
990
|
+
},
|
|
991
|
+
locations: [{
|
|
992
|
+
physicalLocation: {
|
|
993
|
+
artifactLocation: {
|
|
994
|
+
uri: filePath,
|
|
995
|
+
uriBaseId: '%SRCROOT%'
|
|
996
|
+
},
|
|
997
|
+
region: {
|
|
998
|
+
startLine: (issue.line || 0) + 1, // SARIF uses 1-indexed lines
|
|
999
|
+
startColumn: (issue.column || 0) + 1,
|
|
1000
|
+
endLine: (issue.endLine || issue.line || 0) + 1,
|
|
1001
|
+
endColumn: (issue.endColumn || issue.column || 0) + 1,
|
|
1002
|
+
snippet: issue.line_content ? { text: issue.line_content } : undefined
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}],
|
|
1006
|
+
...(issue.suggested_fix?.fixed && {
|
|
1007
|
+
fixes: [{
|
|
1008
|
+
description: {
|
|
1009
|
+
text: issue.suggested_fix.description
|
|
1010
|
+
},
|
|
1011
|
+
artifactChanges: [{
|
|
1012
|
+
artifactLocation: {
|
|
1013
|
+
uri: filePath
|
|
1014
|
+
},
|
|
1015
|
+
replacements: [{
|
|
1016
|
+
deletedRegion: {
|
|
1017
|
+
startLine: (issue.line || 0) + 1,
|
|
1018
|
+
startColumn: 1,
|
|
1019
|
+
endLine: (issue.line || 0) + 1,
|
|
1020
|
+
endColumn: (issue.suggested_fix.original?.length || 0) + 1
|
|
1021
|
+
},
|
|
1022
|
+
insertedContent: {
|
|
1023
|
+
text: issue.suggested_fix.fixed
|
|
1024
|
+
}
|
|
1025
|
+
}]
|
|
1026
|
+
}]
|
|
1027
|
+
}]
|
|
1028
|
+
}),
|
|
1029
|
+
properties: {
|
|
1030
|
+
...(issue.metadata?.cwe && { cwe: issue.metadata.cwe }),
|
|
1031
|
+
...(issue.metadata?.owasp && { owasp: issue.metadata.owasp })
|
|
1032
|
+
}
|
|
1033
|
+
}));
|
|
1034
|
+
|
|
1035
|
+
return {
|
|
1036
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
1037
|
+
version: '2.1.0',
|
|
1038
|
+
runs: [{
|
|
1039
|
+
tool: {
|
|
1040
|
+
driver: {
|
|
1041
|
+
name: 'agent-security-scanner-mcp',
|
|
1042
|
+
version: '2.0.7',
|
|
1043
|
+
informationUri: 'https://github.com/sinewaveai/agent-security-scanner-mcp',
|
|
1044
|
+
rules: Array.from(rulesMap.values())
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
results,
|
|
1048
|
+
invocations: [{
|
|
1049
|
+
executionSuccessful: true,
|
|
1050
|
+
endTimeUtc: new Date().toISOString()
|
|
1051
|
+
}],
|
|
1052
|
+
artifacts: [{
|
|
1053
|
+
location: {
|
|
1054
|
+
uri: filePath,
|
|
1055
|
+
uriBaseId: '%SRCROOT%'
|
|
1056
|
+
},
|
|
1057
|
+
sourceLanguage: language
|
|
1058
|
+
}]
|
|
1059
|
+
}]
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
807
1063
|
// Register scan_security tool
|
|
808
1064
|
server.tool(
|
|
809
1065
|
"scan_security",
|
|
810
1066
|
"Scan a file for security vulnerabilities and return issues with suggested fixes",
|
|
811
1067
|
{
|
|
812
|
-
file_path: z.string().describe("Path to the file to scan")
|
|
1068
|
+
file_path: z.string().describe("Path to the file to scan"),
|
|
1069
|
+
output_format: z.enum(['json', 'sarif']).optional().describe("Output format: 'json' (default) or 'sarif' for GitHub/GitLab integration")
|
|
813
1070
|
},
|
|
814
|
-
async ({ file_path }) => {
|
|
1071
|
+
async ({ file_path, output_format = 'json' }) => {
|
|
815
1072
|
if (!existsSync(file_path)) {
|
|
816
1073
|
return {
|
|
817
1074
|
content: [{ type: "text", text: JSON.stringify({ error: "File not found" }) }]
|
|
@@ -842,6 +1099,18 @@ server.tool(
|
|
|
842
1099
|
};
|
|
843
1100
|
});
|
|
844
1101
|
|
|
1102
|
+
// Return SARIF format if requested (for GitHub/GitLab integration)
|
|
1103
|
+
if (output_format === 'sarif') {
|
|
1104
|
+
const sarif = convertToSarif(file_path, language, enhancedIssues);
|
|
1105
|
+
return {
|
|
1106
|
+
content: [{
|
|
1107
|
+
type: "text",
|
|
1108
|
+
text: JSON.stringify(sarif, null, 2)
|
|
1109
|
+
}]
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// Default JSON format
|
|
845
1114
|
return {
|
|
846
1115
|
content: [{
|
|
847
1116
|
type: "text",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"mcpName": "io.github.sinewaveai/agent-security-scanner-mcp",
|
|
5
5
|
"description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 359 vulnerability rules with auto-fix. For Claude Code, Cursor, Windsurf, Cline.",
|
|
6
6
|
"main": "index.js",
|
|
@@ -52,7 +52,10 @@
|
|
|
52
52
|
"zed",
|
|
53
53
|
"prompt-firewall",
|
|
54
54
|
"auto-fix",
|
|
55
|
-
"hallucination"
|
|
55
|
+
"hallucination",
|
|
56
|
+
"sarif",
|
|
57
|
+
"github-code-scanning",
|
|
58
|
+
"gitlab-sast"
|
|
56
59
|
],
|
|
57
60
|
"author": "Sinewave AI <divya@sinewave.ai>",
|
|
58
61
|
"license": "MIT",
|