agent-security-scanner-mcp 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,20 @@
1
1
  // src/tools/scan-security.js
2
2
  import { z } from "zod";
3
3
  import { existsSync, readFileSync } from "fs";
4
- import { detectLanguage, runAnalyzerAsync, generateFix, toSarif, getEngineMode } from '../utils.js';
4
+ import { dirname } from "path";
5
+ import { detectLanguage, runAnalyzerAsync, generateFix, toSarif, getEngineMode, extractImports, isTestFile } from '../utils.js';
5
6
  import { deduplicateFindings } from '../dedup.js';
6
7
  import { applyContextFilter, detectFrameworks, applyFrameworkAdjustments } from '../context.js';
7
8
  import { loadConfig, shouldExcludeFile, applyConfig } from '../config.js';
9
+ import { discoverProjectContext } from './project-context.js';
8
10
 
9
11
  export const scanSecuritySchema = {
10
12
  file_path: z.string().describe("Path to the file to scan"),
11
13
  output_format: z.enum(['json', 'sarif']).optional().describe("Output format: 'json' (default) or 'sarif' for GitHub/GitLab integration"),
12
14
  verbosity: z.enum(['minimal', 'compact', 'full']).optional().describe("Response detail level: 'minimal' (counts only), 'compact' (default, actionable info), 'full' (complete metadata)"),
13
- engine: z.enum(['auto', 'ast', 'regex']).optional().describe("Analysis engine: 'auto' (default, AST with regex fallback), 'ast' (tree-sitter only), 'regex' (regex only)")
15
+ engine: z.enum(['auto', 'ast', 'regex']).optional().describe("Analysis engine: 'auto' (default, AST with regex fallback), 'ast' (tree-sitter only), 'regex' (regex only)"),
16
+ project_context: z.boolean().optional().describe("Include project context (framework, security middleware, dependencies)"),
17
+ include_context: z.boolean().optional().describe("Include surrounding code context for each issue")
14
18
  };
15
19
 
16
20
  // Verbosity formatters
@@ -58,7 +62,7 @@ function formatFull(file_path, language, issues) {
58
62
  };
59
63
  }
60
64
 
61
- export async function scanSecurity({ file_path, output_format, verbosity, engine }) {
65
+ export async function scanSecurity({ file_path, output_format, verbosity, engine, project_context, include_context }) {
62
66
  if (!existsSync(file_path)) {
63
67
  return {
64
68
  content: [{ type: "text", text: JSON.stringify({ error: "File not found" }) }]
@@ -101,15 +105,30 @@ export async function scanSecurity({ file_path, output_format, verbosity, engine
101
105
  // Apply .scannerrc configuration (rule suppression, severity/confidence thresholds)
102
106
  const issues = applyConfig(frameworkAdjusted, file_path, config);
103
107
 
104
- // Enhance issues with fix suggestions
108
+ // Enhance issues with fix suggestions and optional surrounding context
105
109
  const enhancedIssues = issues.map(issue => {
106
110
  const line = lines[issue.line] || '';
107
111
  const fix = generateFix(issue, line, language);
108
- return {
112
+ const enhanced = {
109
113
  ...issue,
110
114
  line_content: line.trim(),
111
115
  suggested_fix: fix
112
116
  };
117
+
118
+ if (include_context) {
119
+ const lineIdx = issue.line;
120
+ const contextLines = 3;
121
+ enhanced.context_before = [];
122
+ enhanced.context_after = [];
123
+ for (let i = Math.max(0, lineIdx - contextLines); i < lineIdx; i++) {
124
+ enhanced.context_before.push({ line: i + 1, content: lines[i] || '' });
125
+ }
126
+ for (let i = lineIdx + 1; i <= Math.min(lines.length - 1, lineIdx + contextLines); i++) {
127
+ enhanced.context_after.push({ line: i + 1, content: lines[i] || '' });
128
+ }
129
+ }
130
+
131
+ return enhanced;
113
132
  });
114
133
 
115
134
  // Determine verbosity (default: compact)
@@ -139,6 +158,14 @@ export async function scanSecurity({ file_path, output_format, verbosity, engine
139
158
  result = formatCompact(file_path, language, enhancedIssues);
140
159
  }
141
160
 
161
+ // Attach project context if requested
162
+ if (project_context) {
163
+ const projectDir = dirname(file_path);
164
+ result.project = discoverProjectContext(projectDir);
165
+ result.is_test_file = isTestFile(file_path);
166
+ result.file_imports = extractImports(content, language);
167
+ }
168
+
142
169
  return {
143
170
  content: [{
144
171
  type: "text",