@darrenjcoxon/codeguard 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/agent-report.d.ts +36 -0
- package/dist/agent-report.d.ts.map +1 -0
- package/dist/agent-report.js +329 -0
- package/dist/agent-report.js.map +1 -0
- package/dist/ai-summary.d.ts +55 -0
- package/dist/ai-summary.d.ts.map +1 -0
- package/dist/ai-summary.js +267 -0
- package/dist/ai-summary.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +199 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator.d.ts +63 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +331 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/scanners/complexity.d.ts +29 -0
- package/dist/scanners/complexity.d.ts.map +1 -0
- package/dist/scanners/complexity.js +378 -0
- package/dist/scanners/complexity.js.map +1 -0
- package/dist/scanners/eslint.d.ts +21 -0
- package/dist/scanners/eslint.d.ts.map +1 -0
- package/dist/scanners/eslint.js +196 -0
- package/dist/scanners/eslint.js.map +1 -0
- package/dist/scanners/gitleaks.d.ts +21 -0
- package/dist/scanners/gitleaks.d.ts.map +1 -0
- package/dist/scanners/gitleaks.js +158 -0
- package/dist/scanners/gitleaks.js.map +1 -0
- package/dist/scanners/index.d.ts +56 -0
- package/dist/scanners/index.d.ts.map +1 -0
- package/dist/scanners/index.js +71 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/scanners/npm-audit.d.ts +19 -0
- package/dist/scanners/npm-audit.d.ts.map +1 -0
- package/dist/scanners/npm-audit.js +176 -0
- package/dist/scanners/npm-audit.js.map +1 -0
- package/dist/scanners/semgrep.d.ts +22 -0
- package/dist/scanners/semgrep.d.ts.map +1 -0
- package/dist/scanners/semgrep.js +175 -0
- package/dist/scanners/semgrep.js.map +1 -0
- package/dist/types.d.ts +522 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +194 -0
- package/dist/types.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Summary Generator
|
|
3
|
+
*
|
|
4
|
+
* Uses Claude to generate intelligent summaries and recommendations
|
|
5
|
+
*/
|
|
6
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
7
|
+
export class AISummaryGenerator {
|
|
8
|
+
client = null;
|
|
9
|
+
model;
|
|
10
|
+
maxTokens;
|
|
11
|
+
constructor(config = {}) {
|
|
12
|
+
const apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
13
|
+
if (apiKey) {
|
|
14
|
+
this.client = new Anthropic({ apiKey });
|
|
15
|
+
}
|
|
16
|
+
this.model = config.model || 'claude-sonnet-4-20250514';
|
|
17
|
+
this.maxTokens = config.maxTokens || 1024;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if AI summarization is available
|
|
21
|
+
*/
|
|
22
|
+
isAvailable() {
|
|
23
|
+
return this.client !== null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate an AI summary of the scan report
|
|
27
|
+
*/
|
|
28
|
+
async generateSummary(report) {
|
|
29
|
+
if (!this.client) {
|
|
30
|
+
return 'AI summary unavailable (no API key configured)';
|
|
31
|
+
}
|
|
32
|
+
const prompt = this.buildSummaryPrompt(report);
|
|
33
|
+
try {
|
|
34
|
+
const response = await this.client.messages.create({
|
|
35
|
+
model: this.model,
|
|
36
|
+
max_tokens: this.maxTokens,
|
|
37
|
+
messages: [
|
|
38
|
+
{
|
|
39
|
+
role: 'user',
|
|
40
|
+
content: prompt
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
const textContent = response.content.find(c => c.type === 'text');
|
|
45
|
+
return textContent?.text || 'Unable to generate summary';
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error('AI summary generation failed:', error);
|
|
49
|
+
return 'AI summary generation failed';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generate AI recommendations for fixing issues
|
|
54
|
+
*/
|
|
55
|
+
async generateRecommendations(report) {
|
|
56
|
+
if (!this.client) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const prompt = this.buildRecommendationsPrompt(report);
|
|
60
|
+
try {
|
|
61
|
+
const response = await this.client.messages.create({
|
|
62
|
+
model: this.model,
|
|
63
|
+
max_tokens: this.maxTokens,
|
|
64
|
+
messages: [
|
|
65
|
+
{
|
|
66
|
+
role: 'user',
|
|
67
|
+
content: prompt
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
const textContent = response.content.find(c => c.type === 'text');
|
|
72
|
+
if (!textContent?.text)
|
|
73
|
+
return [];
|
|
74
|
+
// Parse numbered recommendations
|
|
75
|
+
const lines = textContent.text.split('\n');
|
|
76
|
+
return lines
|
|
77
|
+
.filter(line => /^\d+\./.test(line.trim()))
|
|
78
|
+
.map(line => line.replace(/^\d+\.\s*/, '').trim());
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('AI recommendations generation failed:', error);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generate a PR comment summary
|
|
87
|
+
*/
|
|
88
|
+
async generatePRComment(report) {
|
|
89
|
+
if (!this.client) {
|
|
90
|
+
return this.generateFallbackPRComment(report);
|
|
91
|
+
}
|
|
92
|
+
const prompt = this.buildPRCommentPrompt(report);
|
|
93
|
+
try {
|
|
94
|
+
const response = await this.client.messages.create({
|
|
95
|
+
model: this.model,
|
|
96
|
+
max_tokens: 800,
|
|
97
|
+
messages: [
|
|
98
|
+
{
|
|
99
|
+
role: 'user',
|
|
100
|
+
content: prompt
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
});
|
|
104
|
+
const textContent = response.content.find(c => c.type === 'text');
|
|
105
|
+
return textContent?.text || this.generateFallbackPRComment(report);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return this.generateFallbackPRComment(report);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Build the summary prompt
|
|
113
|
+
*/
|
|
114
|
+
buildSummaryPrompt(report) {
|
|
115
|
+
const findingsSummary = this.summarizeFindings(report.findings);
|
|
116
|
+
return `You are a security and code quality expert. Analyze this code scan report and provide a concise executive summary.
|
|
117
|
+
|
|
118
|
+
## Scan Results
|
|
119
|
+
|
|
120
|
+
**Total Findings:** ${report.summary.totalFindings}
|
|
121
|
+
|
|
122
|
+
**By Severity:**
|
|
123
|
+
${Object.entries(report.summary.bySeverity)
|
|
124
|
+
.map(([sev, count]) => `- ${sev}: ${count}`)
|
|
125
|
+
.join('\n')}
|
|
126
|
+
|
|
127
|
+
**By Category:**
|
|
128
|
+
${Object.entries(report.summary.byCategory)
|
|
129
|
+
.map(([cat, count]) => `- ${cat}: ${count}`)
|
|
130
|
+
.join('\n')}
|
|
131
|
+
|
|
132
|
+
**Quality Gate:** ${report.summary.passesQualityGate ? 'PASSED' : 'FAILED'}
|
|
133
|
+
|
|
134
|
+
## Top Findings
|
|
135
|
+
|
|
136
|
+
${findingsSummary}
|
|
137
|
+
|
|
138
|
+
## Instructions
|
|
139
|
+
|
|
140
|
+
Provide a 2-3 paragraph executive summary that:
|
|
141
|
+
1. Highlights the most critical issues that need immediate attention
|
|
142
|
+
2. Identifies patterns or systemic issues in the codebase
|
|
143
|
+
3. Gives an overall assessment of the code's security and quality posture
|
|
144
|
+
|
|
145
|
+
Be direct and actionable. Focus on business impact.`;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Build the recommendations prompt
|
|
149
|
+
*/
|
|
150
|
+
buildRecommendationsPrompt(report) {
|
|
151
|
+
const findingsSummary = this.summarizeFindings(report.findings.slice(0, 20));
|
|
152
|
+
return `You are a security and code quality expert. Based on this scan report, provide prioritized recommendations.
|
|
153
|
+
|
|
154
|
+
## Findings
|
|
155
|
+
|
|
156
|
+
${findingsSummary}
|
|
157
|
+
|
|
158
|
+
## Instructions
|
|
159
|
+
|
|
160
|
+
Provide exactly 5 prioritized recommendations in this format:
|
|
161
|
+
1. [Priority 1 recommendation - most critical]
|
|
162
|
+
2. [Priority 2 recommendation]
|
|
163
|
+
3. [Priority 3 recommendation]
|
|
164
|
+
4. [Priority 4 recommendation]
|
|
165
|
+
5. [Priority 5 recommendation]
|
|
166
|
+
|
|
167
|
+
Each recommendation should be:
|
|
168
|
+
- Specific and actionable
|
|
169
|
+
- Include the expected impact
|
|
170
|
+
- Be achievable within a sprint
|
|
171
|
+
|
|
172
|
+
Focus on high-impact fixes first, then patterns that would prevent future issues.`;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Build the PR comment prompt
|
|
176
|
+
*/
|
|
177
|
+
buildPRCommentPrompt(report) {
|
|
178
|
+
const critical = report.findings.filter(f => f.severity === 'critical');
|
|
179
|
+
const high = report.findings.filter(f => f.severity === 'high');
|
|
180
|
+
return `Generate a GitHub/GitLab PR comment summarizing this security scan. Use markdown formatting.
|
|
181
|
+
|
|
182
|
+
**Results:**
|
|
183
|
+
- Total findings: ${report.summary.totalFindings}
|
|
184
|
+
- Critical: ${critical.length}
|
|
185
|
+
- High: ${high.length}
|
|
186
|
+
- Quality Gate: ${report.summary.passesQualityGate ? '✅ Passed' : '❌ Failed'}
|
|
187
|
+
|
|
188
|
+
**Critical Issues:**
|
|
189
|
+
${critical.slice(0, 3).map(f => `- ${f.title} in ${f.file}`).join('\n') || 'None'}
|
|
190
|
+
|
|
191
|
+
**High Priority Issues:**
|
|
192
|
+
${high.slice(0, 3).map(f => `- ${f.title} in ${f.file}`).join('\n') || 'None'}
|
|
193
|
+
|
|
194
|
+
Create a concise, developer-friendly PR comment (max 300 words) that:
|
|
195
|
+
1. Shows the quality gate status prominently
|
|
196
|
+
2. Lists blocking issues (if any)
|
|
197
|
+
3. Provides a brief action summary
|
|
198
|
+
4. Uses appropriate emojis for readability
|
|
199
|
+
|
|
200
|
+
Do not include any preamble, just output the markdown comment.`;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Summarize findings for prompts
|
|
204
|
+
*/
|
|
205
|
+
summarizeFindings(findings) {
|
|
206
|
+
return findings
|
|
207
|
+
.slice(0, 15)
|
|
208
|
+
.map(f => `- [${f.severity.toUpperCase()}] ${f.category}: ${f.title}
|
|
209
|
+
File: ${f.file}${f.line ? `:${f.line}` : ''}
|
|
210
|
+
${f.description.slice(0, 200)}${f.description.length > 200 ? '...' : ''}`)
|
|
211
|
+
.join('\n\n');
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Generate a fallback PR comment without AI
|
|
215
|
+
*/
|
|
216
|
+
generateFallbackPRComment(report) {
|
|
217
|
+
const { summary, findings } = report;
|
|
218
|
+
const critical = findings.filter(f => f.severity === 'critical');
|
|
219
|
+
const high = findings.filter(f => f.severity === 'high');
|
|
220
|
+
const secrets = findings.filter(f => f.category === 'secret');
|
|
221
|
+
const gateEmoji = summary.passesQualityGate ? '✅' : '❌';
|
|
222
|
+
const gateText = summary.passesQualityGate ? 'Passed' : 'Failed';
|
|
223
|
+
let comment = `## 🔍 CodeGuard Security Scan
|
|
224
|
+
|
|
225
|
+
### ${gateEmoji} Quality Gate: **${gateText}**
|
|
226
|
+
|
|
227
|
+
| Severity | Count |
|
|
228
|
+
|----------|-------|
|
|
229
|
+
| 🔴 Critical | ${summary.bySeverity['critical'] || 0} |
|
|
230
|
+
| 🟠 High | ${summary.bySeverity['high'] || 0} |
|
|
231
|
+
| 🟡 Medium | ${summary.bySeverity['medium'] || 0} |
|
|
232
|
+
| 🔵 Low | ${summary.bySeverity['low'] || 0} |
|
|
233
|
+
|
|
234
|
+
**Total Findings:** ${summary.totalFindings}
|
|
235
|
+
`;
|
|
236
|
+
if (critical.length > 0) {
|
|
237
|
+
comment += `
|
|
238
|
+
### 🚨 Critical Issues (Blocking)
|
|
239
|
+
|
|
240
|
+
${critical.slice(0, 5).map(f => `- **${f.title}** in \`${f.file}\`${f.line ? `:${f.line}` : ''}`).join('\n')}
|
|
241
|
+
`;
|
|
242
|
+
}
|
|
243
|
+
if (secrets.length > 0) {
|
|
244
|
+
comment += `
|
|
245
|
+
### 🔑 Exposed Secrets (Blocking)
|
|
246
|
+
|
|
247
|
+
${secrets.slice(0, 5).map(f => `- **${f.title}** in \`${f.file}\``).join('\n')}
|
|
248
|
+
|
|
249
|
+
⚠️ **Action Required:** Rotate these credentials immediately!
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
if (high.length > 0 && critical.length === 0 && secrets.length === 0) {
|
|
253
|
+
comment += `
|
|
254
|
+
### ⚠️ High Priority Issues
|
|
255
|
+
|
|
256
|
+
${high.slice(0, 5).map(f => `- **${f.title}** in \`${f.file}\`${f.line ? `:${f.line}` : ''}`).join('\n')}
|
|
257
|
+
`;
|
|
258
|
+
}
|
|
259
|
+
comment += `
|
|
260
|
+
---
|
|
261
|
+
*Generated by CodeGuard • [View Full Report](#)*
|
|
262
|
+
`;
|
|
263
|
+
return comment;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export default AISummaryGenerator;
|
|
267
|
+
//# sourceMappingURL=ai-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-summary.js","sourceRoot":"","sources":["../src/ai-summary.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CodeGuard CLI
|
|
4
|
+
*
|
|
5
|
+
* Dead simple: just run `codeguard` in your project directory
|
|
6
|
+
* Get a FIXES.md file to give to your AI coding agent
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { resolve } from 'path';
|
|
11
|
+
import { existsSync, writeFileSync } from 'fs';
|
|
12
|
+
import { Orchestrator } from './orchestrator.js';
|
|
13
|
+
import { AgentReportGenerator } from './agent-report.js';
|
|
14
|
+
import { SCANNER_COVERAGE } from './scanners/index.js';
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name('codeguard')
|
|
18
|
+
.description(`
|
|
19
|
+
🛡️ CodeGuard - Security Scanner for AI Agents
|
|
20
|
+
|
|
21
|
+
Scans your code and generates FIXES.md that you can
|
|
22
|
+
give to Claude Code, Cursor, or any AI coding agent.
|
|
23
|
+
|
|
24
|
+
Just run: codeguard
|
|
25
|
+
`)
|
|
26
|
+
.version('1.0.0');
|
|
27
|
+
// Default action - just scan current directory
|
|
28
|
+
program
|
|
29
|
+
.argument('[path]', 'Path to scan (default: current directory)', '.')
|
|
30
|
+
.option('-o, --output <file>', 'Also save full report to file')
|
|
31
|
+
.option('-f, --format <format>', 'Report format: json, markdown, sarif', 'json')
|
|
32
|
+
.option('--no-fixes', 'Skip generating FIXES.md')
|
|
33
|
+
.option('--json', 'Output JSON only (no FIXES.md)')
|
|
34
|
+
.option('--ci', 'CI mode: fail on high/critical issues')
|
|
35
|
+
.option('-q, --quiet', 'Minimal output')
|
|
36
|
+
.action(async (path, options) => {
|
|
37
|
+
await runScan(path, options);
|
|
38
|
+
});
|
|
39
|
+
// Check command - verify tools are installed
|
|
40
|
+
program
|
|
41
|
+
.command('check')
|
|
42
|
+
.description('Check which scanning tools are installed')
|
|
43
|
+
.action(async () => {
|
|
44
|
+
console.log(chalk.bold('\n🔍 Checking available scanners...\n'));
|
|
45
|
+
const orchestrator = new Orchestrator();
|
|
46
|
+
const available = await orchestrator.checkScanners();
|
|
47
|
+
for (const [name, isAvailable] of Object.entries(available)) {
|
|
48
|
+
const status = isAvailable
|
|
49
|
+
? chalk.green('✓ installed')
|
|
50
|
+
: chalk.yellow('○ not found');
|
|
51
|
+
console.log(` ${name}: ${status}`);
|
|
52
|
+
}
|
|
53
|
+
console.log(chalk.gray('\nInstall missing tools:'));
|
|
54
|
+
console.log(chalk.gray(' semgrep: pip install semgrep'));
|
|
55
|
+
console.log(chalk.gray(' gitleaks: brew install gitleaks'));
|
|
56
|
+
console.log();
|
|
57
|
+
});
|
|
58
|
+
// List scanners
|
|
59
|
+
program
|
|
60
|
+
.command('info')
|
|
61
|
+
.description('Show what CodeGuard scans for')
|
|
62
|
+
.action(() => {
|
|
63
|
+
console.log(chalk.bold('\n🛡️ CodeGuard Scanners\n'));
|
|
64
|
+
for (const [name, info] of Object.entries(SCANNER_COVERAGE)) {
|
|
65
|
+
console.log(chalk.cyan.bold(` ${name}`));
|
|
66
|
+
console.log(chalk.white(` ${info.description}`));
|
|
67
|
+
console.log(chalk.gray(` Finds: ${info.categories.join(', ')}`));
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
// The scan logic
|
|
72
|
+
async function runScan(path, options) {
|
|
73
|
+
const targetPath = resolve(path);
|
|
74
|
+
const quiet = options.quiet || options.json;
|
|
75
|
+
if (!existsSync(targetPath)) {
|
|
76
|
+
console.error(chalk.red(`Error: Path not found: ${targetPath}`));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
if (!quiet) {
|
|
80
|
+
console.log(chalk.bold('\n🛡️ CodeGuard Security Scan\n'));
|
|
81
|
+
console.log(chalk.gray(` Scanning: ${targetPath}\n`));
|
|
82
|
+
}
|
|
83
|
+
const orchestrator = new Orchestrator({
|
|
84
|
+
parallel: true,
|
|
85
|
+
verbose: !quiet
|
|
86
|
+
});
|
|
87
|
+
try {
|
|
88
|
+
const report = await orchestrator.scan({ path: targetPath });
|
|
89
|
+
// Summary
|
|
90
|
+
const critical = report.summary.bySeverity['critical'] || 0;
|
|
91
|
+
const high = report.summary.bySeverity['high'] || 0;
|
|
92
|
+
const medium = report.summary.bySeverity['medium'] || 0;
|
|
93
|
+
const low = report.summary.bySeverity['low'] || 0;
|
|
94
|
+
if (!quiet) {
|
|
95
|
+
console.log(chalk.bold('\n📊 Results\n'));
|
|
96
|
+
if (critical > 0)
|
|
97
|
+
console.log(chalk.red(` 🔴 Critical: ${critical}`));
|
|
98
|
+
if (high > 0)
|
|
99
|
+
console.log(chalk.yellow(` 🟠 High: ${high}`));
|
|
100
|
+
if (medium > 0)
|
|
101
|
+
console.log(chalk.blue(` 🟡 Medium: ${medium}`));
|
|
102
|
+
if (low > 0)
|
|
103
|
+
console.log(chalk.gray(` 🔵 Low: ${low}`));
|
|
104
|
+
if (report.findings.length === 0) {
|
|
105
|
+
console.log(chalk.green(' ✨ No issues found!'));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Generate FIXES.md (unless disabled)
|
|
109
|
+
if (options.fixes !== false && !options.json && report.findings.length > 0) {
|
|
110
|
+
const fixesPath = resolve(targetPath, 'FIXES.md');
|
|
111
|
+
const agentGenerator = new AgentReportGenerator();
|
|
112
|
+
agentGenerator.generateAndSave(report, fixesPath, {
|
|
113
|
+
includeSnippets: true,
|
|
114
|
+
groupByFile: true
|
|
115
|
+
});
|
|
116
|
+
if (!quiet) {
|
|
117
|
+
console.log(chalk.green(`\n✅ Created: FIXES.md`));
|
|
118
|
+
console.log(chalk.cyan(` Give this file to Claude Code or Cursor to fix the issues\n`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Save full report if requested
|
|
122
|
+
if (options.output) {
|
|
123
|
+
const outputPath = resolve(options.output);
|
|
124
|
+
if (options.format === 'markdown') {
|
|
125
|
+
writeFileSync(outputPath, generateMarkdown(report));
|
|
126
|
+
}
|
|
127
|
+
else if (options.format === 'sarif') {
|
|
128
|
+
writeFileSync(outputPath, JSON.stringify(generateSarif(report), null, 2));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
132
|
+
}
|
|
133
|
+
if (!quiet) {
|
|
134
|
+
console.log(chalk.gray(` Report saved: ${outputPath}`));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// JSON only mode
|
|
138
|
+
if (options.json) {
|
|
139
|
+
console.log(JSON.stringify(report, null, 2));
|
|
140
|
+
}
|
|
141
|
+
// CI mode - exit with error if issues found
|
|
142
|
+
if (options.ci && (critical > 0 || high > 0)) {
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(chalk.red('\n❌ Scan failed:'), error);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Simple markdown report
|
|
152
|
+
function generateMarkdown(report) {
|
|
153
|
+
let md = `# CodeGuard Security Report\n\n`;
|
|
154
|
+
md += `**Scanned:** ${report.target.path}\n`;
|
|
155
|
+
md += `**Date:** ${new Date(report.timestamp).toLocaleString()}\n\n`;
|
|
156
|
+
md += `## Summary\n\n`;
|
|
157
|
+
md += `- Critical: ${report.summary.bySeverity['critical'] || 0}\n`;
|
|
158
|
+
md += `- High: ${report.summary.bySeverity['high'] || 0}\n`;
|
|
159
|
+
md += `- Medium: ${report.summary.bySeverity['medium'] || 0}\n`;
|
|
160
|
+
md += `- Low: ${report.summary.bySeverity['low'] || 0}\n\n`;
|
|
161
|
+
if (report.findings.length > 0) {
|
|
162
|
+
md += `## Findings\n\n`;
|
|
163
|
+
for (const f of report.findings) {
|
|
164
|
+
md += `### ${f.severity.toUpperCase()}: ${f.title}\n`;
|
|
165
|
+
md += `- File: \`${f.file}:${f.line || ''}\`\n`;
|
|
166
|
+
md += `- ${f.description}\n\n`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return md;
|
|
170
|
+
}
|
|
171
|
+
// SARIF format for GitHub
|
|
172
|
+
function generateSarif(report) {
|
|
173
|
+
return {
|
|
174
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
175
|
+
version: "2.1.0",
|
|
176
|
+
runs: [{
|
|
177
|
+
tool: {
|
|
178
|
+
driver: {
|
|
179
|
+
name: "CodeGuard",
|
|
180
|
+
version: "1.0.0",
|
|
181
|
+
informationUri: "https://github.com/darrenjcoxon/codeguard"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
results: report.findings.map(f => ({
|
|
185
|
+
ruleId: f.ruleId || f.title,
|
|
186
|
+
level: f.severity === 'critical' || f.severity === 'high' ? 'error' : 'warning',
|
|
187
|
+
message: { text: f.description },
|
|
188
|
+
locations: [{
|
|
189
|
+
physicalLocation: {
|
|
190
|
+
artifactLocation: { uri: f.file },
|
|
191
|
+
region: { startLine: f.line || 1 }
|
|
192
|
+
}
|
|
193
|
+
}]
|
|
194
|
+
}))
|
|
195
|
+
}]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
program.parse();
|
|
199
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC;;;;;;;GAOZ,CAAC;KACD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,+CAA+C;AAC/C,OAAO;KACJ,QAAQ,CAAC,QAAQ,EAAE,2CAA2C,EAAE,GAAG,CAAC;KACpE,MAAM,CAAC,qBAAqB,EAAE,+BAA+B,CAAC;KAC9D,MAAM,CAAC,uBAAuB,EAAE,sCAAsC,EAAE,MAAM,CAAC;KAC/E,MAAM,CAAC,YAAY,EAAE,0BAA0B,CAAC;KAChD,MAAM,CAAC,QAAQ,EAAE,gCAAgC,CAAC;KAClD,MAAM,CAAC,MAAM,EAAE,uCAAuC,CAAC;KACvD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACvC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAC9B,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEL,6CAA6C;AAC7C,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC,CAAC;IAEjE,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,aAAa,EAAE,CAAC;IAErD,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAG,WAAW;YACxB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC;YAC5B,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC,CAAC,CAAC;AAEL,gBAAgB;AAChB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,+BAA+B,CAAC;KAC5C,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAEvD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,iBAAiB;AACjB,KAAK,UAAU,OAAO,CAAC,IAAY,EAAE,OAAY;IAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAE5C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;QAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,UAAU,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC;QACpC,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,CAAC,KAAK;KAChB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAE7D,UAAU;QACV,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAE1C,IAAI,QAAQ,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC,CAAC;YACxE,IAAI,IAAI,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/D,IAAI,MAAM,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,MAAM,EAAE,CAAC,CAAC,CAAC;YACnE,IAAI,GAAG,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC,CAAC;YAE1D,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3E,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAClD,MAAM,cAAc,GAAG,IAAI,oBAAoB,EAAE,CAAC;YAClD,cAAc,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE;gBAChD,eAAe,EAAE,IAAI;gBACrB,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;YAEH,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAE3C,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBAClC,aAAa,CAAC,UAAU,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,CAAC;iBAAM,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACtC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5E,CAAC;iBAAM,CAAC;gBACN,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,UAAU,EAAE,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,iBAAiB;QACjB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;QAED,4CAA4C;QAC5C,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IAEH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,KAAK,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,yBAAyB;AACzB,SAAS,gBAAgB,CAAC,MAAkB;IAC1C,IAAI,EAAE,GAAG,iCAAiC,CAAC;IAC3C,EAAE,IAAI,gBAAgB,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAC7C,EAAE,IAAI,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,MAAM,CAAC;IAErE,EAAE,IAAI,gBAAgB,CAAC;IACvB,EAAE,IAAI,eAAe,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;IACpE,EAAE,IAAI,WAAW,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IAC5D,EAAE,IAAI,aAAa,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;IAChE,EAAE,IAAI,UAAU,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;IAE5D,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,EAAE,IAAI,iBAAiB,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,EAAE,IAAI,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC;YACtD,EAAE,IAAI,aAAa,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,MAAM,CAAC;YAChD,EAAE,IAAI,KAAK,CAAC,CAAC,WAAW,MAAM,CAAC;QACjC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,0BAA0B;AAC1B,SAAS,aAAa,CAAC,MAAkB;IACvC,OAAO;QACL,OAAO,EAAE,gGAAgG;QACzG,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,CAAC;gBACL,IAAI,EAAE;oBACJ,MAAM,EAAE;wBACN,IAAI,EAAE,WAAW;wBACjB,OAAO,EAAE,OAAO;wBAChB,cAAc,EAAE,2CAA2C;qBAC5D;iBACF;gBACD,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACjC,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK;oBAC3B,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;oBAC/E,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;oBAChC,SAAS,EAAE,CAAC;4BACV,gBAAgB,EAAE;gCAChB,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE;gCACjC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE;6BACnC;yBACF,CAAC;iBACH,CAAC,CAAC;aACJ,CAAC;KACH,CAAC;AACJ,CAAC;AAED,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeGuard - AI-Powered Code Security & Quality Scanner
|
|
3
|
+
*
|
|
4
|
+
* A pluggable orchestrator that combines multiple security and quality
|
|
5
|
+
* scanning tools with AI-powered analysis.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { Orchestrator, ScanTarget } from 'codeguard';
|
|
10
|
+
*
|
|
11
|
+
* const orchestrator = new Orchestrator();
|
|
12
|
+
* const target: ScanTarget = { path: './my-project' };
|
|
13
|
+
* const report = await orchestrator.scan(target);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export { Orchestrator, type OrchestratorConfig } from './orchestrator.js';
|
|
17
|
+
export { AISummaryGenerator, type AISummaryConfig } from './ai-summary.js';
|
|
18
|
+
export { AgentReportGenerator, type AgentReportOptions } from './agent-report.js';
|
|
19
|
+
export { type Finding, type ScanTarget, type ScanReport, type ScannerResult, type ScannerConfig, type Scanner, type Reporter, type QualityGate, type SeverityLevel, type FindingCategory, DEFAULT_QUALITY_GATE, generateFingerprint, severityWeight } from './types.js';
|
|
20
|
+
export { SemgrepScanner, GitleaksScanner, NpmAuditScanner, ESLintScanner, ComplexityScanner, getAllScanners, getScannersByCategory, SCANNER_COVERAGE } from './scanners/index.js';
|
|
21
|
+
import { Orchestrator } from './orchestrator.js';
|
|
22
|
+
export default Orchestrator;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGlF,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,eAAe,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeGuard - AI-Powered Code Security & Quality Scanner
|
|
3
|
+
*
|
|
4
|
+
* A pluggable orchestrator that combines multiple security and quality
|
|
5
|
+
* scanning tools with AI-powered analysis.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { Orchestrator, ScanTarget } from 'codeguard';
|
|
10
|
+
*
|
|
11
|
+
* const orchestrator = new Orchestrator();
|
|
12
|
+
* const target: ScanTarget = { path: './my-project' };
|
|
13
|
+
* const report = await orchestrator.scan(target);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
// Core exports
|
|
17
|
+
export { Orchestrator } from './orchestrator.js';
|
|
18
|
+
export { AISummaryGenerator } from './ai-summary.js';
|
|
19
|
+
export { AgentReportGenerator } from './agent-report.js';
|
|
20
|
+
// Types
|
|
21
|
+
export { DEFAULT_QUALITY_GATE, generateFingerprint, severityWeight } from './types.js';
|
|
22
|
+
// Scanners
|
|
23
|
+
export { SemgrepScanner, GitleaksScanner, NpmAuditScanner, ESLintScanner, ComplexityScanner, getAllScanners, getScannersByCategory, SCANNER_COVERAGE } from './scanners/index.js';
|
|
24
|
+
// Default export for convenience
|
|
25
|
+
import { Orchestrator } from './orchestrator.js';
|
|
26
|
+
export default Orchestrator;
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAe;AACf,OAAO,EAAE,YAAY,EAA2B,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAwB,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAA2B,MAAM,mBAAmB,CAAC;AAElF,QAAQ;AACR,OAAO,EAWL,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,WAAW;AACX,OAAO,EACL,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAE7B,iCAAiC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeGuard Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates multiple scanners, aggregates results, and generates reports
|
|
5
|
+
*/
|
|
6
|
+
import { Scanner, ScanTarget, ScanReport, QualityGate } from './types.js';
|
|
7
|
+
export interface OrchestratorConfig {
|
|
8
|
+
scanners?: Scanner[];
|
|
9
|
+
qualityGate?: QualityGate;
|
|
10
|
+
parallel?: boolean;
|
|
11
|
+
verbose?: boolean;
|
|
12
|
+
categories?: string[];
|
|
13
|
+
maxFindings?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class Orchestrator {
|
|
16
|
+
private scanners;
|
|
17
|
+
private qualityGate;
|
|
18
|
+
private parallel;
|
|
19
|
+
private verbose;
|
|
20
|
+
constructor(config?: OrchestratorConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Check which scanners are available (for CLI check command)
|
|
23
|
+
*/
|
|
24
|
+
checkScanners(): Promise<Record<string, boolean>>;
|
|
25
|
+
/**
|
|
26
|
+
* Run all scanners against the target
|
|
27
|
+
*/
|
|
28
|
+
scan(target: ScanTarget): Promise<ScanReport>;
|
|
29
|
+
/**
|
|
30
|
+
* Check which scanners are available
|
|
31
|
+
*/
|
|
32
|
+
private checkScannerAvailability;
|
|
33
|
+
/**
|
|
34
|
+
* Run scanners in parallel
|
|
35
|
+
*/
|
|
36
|
+
private runScannersParallel;
|
|
37
|
+
/**
|
|
38
|
+
* Run scanners sequentially
|
|
39
|
+
*/
|
|
40
|
+
private runScannersSequential;
|
|
41
|
+
/**
|
|
42
|
+
* Aggregate findings from all scanners and deduplicate
|
|
43
|
+
*/
|
|
44
|
+
private aggregateFindings;
|
|
45
|
+
/**
|
|
46
|
+
* Evaluate quality gate rules
|
|
47
|
+
*/
|
|
48
|
+
private evaluateQualityGate;
|
|
49
|
+
/**
|
|
50
|
+
* Create summary statistics
|
|
51
|
+
*/
|
|
52
|
+
private createSummary;
|
|
53
|
+
/**
|
|
54
|
+
* Print summary to console
|
|
55
|
+
*/
|
|
56
|
+
private printSummary;
|
|
57
|
+
/**
|
|
58
|
+
* Create an empty report (when no scanners available)
|
|
59
|
+
*/
|
|
60
|
+
private createEmptyReport;
|
|
61
|
+
}
|
|
62
|
+
export default Orchestrator;
|
|
63
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EACL,OAAO,EACP,UAAU,EAEV,UAAU,EAEV,WAAW,EAIZ,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAU;gBAEb,MAAM,GAAE,kBAAuB;IAc3C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAQvD;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IA8CnD;;OAEG;YACW,wBAAwB;IA0BtC;;OAEG;YACW,mBAAmB;IAyCjC;;OAEG;YACW,qBAAqB;IAwCnC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAwBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAwDpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAqB1B;AAED,eAAe,YAAY,CAAC"}
|