aura-security 0.4.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 +446 -0
- package/deploy/AWS-DEPLOYMENT.md +358 -0
- package/deploy/terraform/main.tf +362 -0
- package/deploy/terraform/terraform.tfvars.example +6 -0
- package/dist/agents/base.d.ts +44 -0
- package/dist/agents/base.js +96 -0
- package/dist/agents/index.d.ts +14 -0
- package/dist/agents/index.js +17 -0
- package/dist/agents/policy/evaluator.d.ts +15 -0
- package/dist/agents/policy/evaluator.js +183 -0
- package/dist/agents/policy/index.d.ts +12 -0
- package/dist/agents/policy/index.js +15 -0
- package/dist/agents/policy/validator.d.ts +15 -0
- package/dist/agents/policy/validator.js +182 -0
- package/dist/agents/scanners/gitleaks.d.ts +14 -0
- package/dist/agents/scanners/gitleaks.js +155 -0
- package/dist/agents/scanners/grype.d.ts +14 -0
- package/dist/agents/scanners/grype.js +109 -0
- package/dist/agents/scanners/index.d.ts +15 -0
- package/dist/agents/scanners/index.js +27 -0
- package/dist/agents/scanners/npm-audit.d.ts +13 -0
- package/dist/agents/scanners/npm-audit.js +129 -0
- package/dist/agents/scanners/semgrep.d.ts +14 -0
- package/dist/agents/scanners/semgrep.js +131 -0
- package/dist/agents/scanners/trivy.d.ts +14 -0
- package/dist/agents/scanners/trivy.js +122 -0
- package/dist/agents/types.d.ts +137 -0
- package/dist/agents/types.js +91 -0
- package/dist/auditor/index.d.ts +3 -0
- package/dist/auditor/index.js +2 -0
- package/dist/auditor/pipeline.d.ts +19 -0
- package/dist/auditor/pipeline.js +240 -0
- package/dist/auditor/validator.d.ts +17 -0
- package/dist/auditor/validator.js +58 -0
- package/dist/aura/client.d.ts +29 -0
- package/dist/aura/client.js +125 -0
- package/dist/aura/index.d.ts +4 -0
- package/dist/aura/index.js +2 -0
- package/dist/aura/server.d.ts +45 -0
- package/dist/aura/server.js +343 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +1433 -0
- package/dist/client/index.d.ts +41 -0
- package/dist/client/index.js +170 -0
- package/dist/compliance/index.d.ts +40 -0
- package/dist/compliance/index.js +292 -0
- package/dist/database/index.d.ts +77 -0
- package/dist/database/index.js +395 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +762 -0
- package/dist/integrations/aura-scanner.d.ts +69 -0
- package/dist/integrations/aura-scanner.js +155 -0
- package/dist/integrations/aws-scanner.d.ts +63 -0
- package/dist/integrations/aws-scanner.js +624 -0
- package/dist/integrations/config.d.ts +69 -0
- package/dist/integrations/config.js +212 -0
- package/dist/integrations/github.d.ts +45 -0
- package/dist/integrations/github.js +201 -0
- package/dist/integrations/gitlab.d.ts +36 -0
- package/dist/integrations/gitlab.js +110 -0
- package/dist/integrations/index.d.ts +11 -0
- package/dist/integrations/index.js +11 -0
- package/dist/integrations/local-scanner.d.ts +146 -0
- package/dist/integrations/local-scanner.js +1654 -0
- package/dist/integrations/notifications.d.ts +99 -0
- package/dist/integrations/notifications.js +305 -0
- package/dist/integrations/scanners.d.ts +57 -0
- package/dist/integrations/scanners.js +217 -0
- package/dist/integrations/slop-scanner.d.ts +69 -0
- package/dist/integrations/slop-scanner.js +155 -0
- package/dist/integrations/webhook.d.ts +37 -0
- package/dist/integrations/webhook.js +256 -0
- package/dist/orchestrator/index.d.ts +72 -0
- package/dist/orchestrator/index.js +187 -0
- package/dist/output/index.d.ts +152 -0
- package/dist/output/index.js +399 -0
- package/dist/pipeline/index.d.ts +72 -0
- package/dist/pipeline/index.js +313 -0
- package/dist/sbom/index.d.ts +94 -0
- package/dist/sbom/index.js +298 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/input.schema.d.ts +87 -0
- package/dist/schemas/input.schema.js +44 -0
- package/dist/schemas/output.schema.d.ts +115 -0
- package/dist/schemas/output.schema.js +64 -0
- package/dist/serve-visualizer.d.ts +2 -0
- package/dist/serve-visualizer.js +78 -0
- package/dist/slop/client.d.ts +29 -0
- package/dist/slop/client.js +125 -0
- package/dist/slop/index.d.ts +4 -0
- package/dist/slop/index.js +2 -0
- package/dist/slop/server.d.ts +45 -0
- package/dist/slop/server.js +343 -0
- package/dist/types/events.d.ts +62 -0
- package/dist/types/events.js +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/visualizer/index.d.ts +4 -0
- package/dist/visualizer/index.js +181 -0
- package/dist/websocket/index.d.ts +88 -0
- package/dist/websocket/index.js +195 -0
- package/dist/zones/index.d.ts +7 -0
- package/dist/zones/index.js +7 -0
- package/dist/zones/manager.d.ts +101 -0
- package/dist/zones/manager.js +304 -0
- package/dist/zones/types.d.ts +78 -0
- package/dist/zones/types.js +33 -0
- package/package.json +84 -0
- package/visualizer/app.js +0 -0
- package/visualizer/index-minimal.html +1771 -0
- package/visualizer/index.html +2933 -0
- package/visualizer/landing.html +1328 -0
- package/visualizer/styles.css +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface ModuleConfig {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
critical_paths?: string[];
|
|
6
|
+
connections?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface IntegrationConfig {
|
|
9
|
+
github?: {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
token?: string;
|
|
12
|
+
webhook_secret?: string;
|
|
13
|
+
};
|
|
14
|
+
gitlab?: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
token?: string;
|
|
17
|
+
api_url?: string;
|
|
18
|
+
};
|
|
19
|
+
scanners?: {
|
|
20
|
+
snyk?: {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
};
|
|
23
|
+
trivy?: {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
};
|
|
26
|
+
semgrep?: {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
};
|
|
29
|
+
npm_audit?: {
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface AuditorConfig {
|
|
35
|
+
version: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
server: {
|
|
39
|
+
port: number;
|
|
40
|
+
host?: string;
|
|
41
|
+
};
|
|
42
|
+
webhook?: {
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
port: number;
|
|
45
|
+
secret?: string;
|
|
46
|
+
};
|
|
47
|
+
visualizer?: {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
port: number;
|
|
50
|
+
};
|
|
51
|
+
modules: ModuleConfig[];
|
|
52
|
+
policy: {
|
|
53
|
+
critical_assets: string[];
|
|
54
|
+
risk_tolerance: 'low' | 'medium' | 'high';
|
|
55
|
+
};
|
|
56
|
+
integrations?: IntegrationConfig;
|
|
57
|
+
}
|
|
58
|
+
export declare class ConfigLoader {
|
|
59
|
+
private config;
|
|
60
|
+
constructor();
|
|
61
|
+
loadFromFile(filePath: string): AuditorConfig;
|
|
62
|
+
loadFromEnv(): AuditorConfig;
|
|
63
|
+
autoLoad(basePath?: string): AuditorConfig;
|
|
64
|
+
getConfig(): AuditorConfig;
|
|
65
|
+
validate(): string[];
|
|
66
|
+
private parseSimpleYaml;
|
|
67
|
+
private mergeConfig;
|
|
68
|
+
}
|
|
69
|
+
export declare const configLoader: ConfigLoader;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Configuration System - Load and validate auditor configuration
|
|
2
|
+
// Supports: JSON, YAML (with js-yaml), and environment variables
|
|
3
|
+
import { readFileSync, existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
version: '1.0',
|
|
7
|
+
server: {
|
|
8
|
+
port: 3000,
|
|
9
|
+
host: '127.0.0.1'
|
|
10
|
+
},
|
|
11
|
+
webhook: {
|
|
12
|
+
enabled: false,
|
|
13
|
+
port: 3001
|
|
14
|
+
},
|
|
15
|
+
visualizer: {
|
|
16
|
+
enabled: true,
|
|
17
|
+
port: 8080
|
|
18
|
+
},
|
|
19
|
+
modules: [
|
|
20
|
+
{ id: 'auth', name: 'AUTH', description: 'Authentication & Identity', critical_paths: ['**/auth/**', '**/login/**', '**/oauth/**'] },
|
|
21
|
+
{ id: 'database', name: 'DATABASE', description: 'Data Storage', critical_paths: ['**/db/**', '**/database/**', '**/models/**'] },
|
|
22
|
+
{ id: 'api', name: 'API', description: 'External Endpoints', critical_paths: ['**/api/**', '**/routes/**', '**/controllers/**'] },
|
|
23
|
+
{ id: 'infra', name: 'INFRA', description: 'Infrastructure', critical_paths: ['**/*.tf', '**/terraform/**', '**/k8s/**', '**/docker/**'] },
|
|
24
|
+
{ id: 'billing', name: 'BILLING', description: 'Payment Processing', critical_paths: ['**/billing/**', '**/payment/**', '**/stripe/**'] },
|
|
25
|
+
{ id: 'secrets', name: 'SECRETS', description: 'Credentials & Keys', critical_paths: ['**/.env*', '**/secrets/**', '**/config/**'] }
|
|
26
|
+
],
|
|
27
|
+
policy: {
|
|
28
|
+
critical_assets: ['auth', 'billing', 'database', 'secrets'],
|
|
29
|
+
risk_tolerance: 'medium'
|
|
30
|
+
},
|
|
31
|
+
integrations: {
|
|
32
|
+
github: { enabled: false },
|
|
33
|
+
gitlab: { enabled: false },
|
|
34
|
+
scanners: {
|
|
35
|
+
snyk: { enabled: false },
|
|
36
|
+
trivy: { enabled: false },
|
|
37
|
+
semgrep: { enabled: false },
|
|
38
|
+
npm_audit: { enabled: true }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
export class ConfigLoader {
|
|
43
|
+
config;
|
|
44
|
+
constructor() {
|
|
45
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
46
|
+
}
|
|
47
|
+
// Load from file
|
|
48
|
+
loadFromFile(filePath) {
|
|
49
|
+
if (!existsSync(filePath)) {
|
|
50
|
+
console.warn(`[Config] File not found: ${filePath}, using defaults`);
|
|
51
|
+
return this.config;
|
|
52
|
+
}
|
|
53
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
54
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
55
|
+
let parsed;
|
|
56
|
+
if (ext === 'json') {
|
|
57
|
+
parsed = JSON.parse(content);
|
|
58
|
+
}
|
|
59
|
+
else if (ext === 'yaml' || ext === 'yml') {
|
|
60
|
+
// Simple YAML parsing (key: value format)
|
|
61
|
+
parsed = this.parseSimpleYaml(content);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
throw new Error(`Unsupported config format: ${ext}`);
|
|
65
|
+
}
|
|
66
|
+
this.config = this.mergeConfig(this.config, parsed);
|
|
67
|
+
return this.config;
|
|
68
|
+
}
|
|
69
|
+
// Load from environment variables
|
|
70
|
+
loadFromEnv() {
|
|
71
|
+
const env = process.env;
|
|
72
|
+
if (env.AURA_PORT) {
|
|
73
|
+
this.config.server.port = parseInt(env.AURA_PORT, 10);
|
|
74
|
+
}
|
|
75
|
+
if (env.AURA_HOST) {
|
|
76
|
+
this.config.server.host = env.AURA_HOST;
|
|
77
|
+
}
|
|
78
|
+
if (env.WEBHOOK_PORT) {
|
|
79
|
+
this.config.webhook = { ...this.config.webhook, enabled: true, port: parseInt(env.WEBHOOK_PORT, 10) };
|
|
80
|
+
}
|
|
81
|
+
if (env.WEBHOOK_SECRET) {
|
|
82
|
+
this.config.webhook = { ...this.config.webhook, secret: env.WEBHOOK_SECRET };
|
|
83
|
+
}
|
|
84
|
+
if (env.VISUALIZER_PORT) {
|
|
85
|
+
this.config.visualizer = { ...this.config.visualizer, port: parseInt(env.VISUALIZER_PORT, 10) };
|
|
86
|
+
}
|
|
87
|
+
if (env.GITHUB_TOKEN) {
|
|
88
|
+
this.config.integrations = {
|
|
89
|
+
...this.config.integrations,
|
|
90
|
+
github: { enabled: true, token: env.GITHUB_TOKEN }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (env.GITLAB_TOKEN) {
|
|
94
|
+
this.config.integrations = {
|
|
95
|
+
...this.config.integrations,
|
|
96
|
+
gitlab: { enabled: true, token: env.GITLAB_TOKEN }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (env.RISK_TOLERANCE) {
|
|
100
|
+
this.config.policy.risk_tolerance = env.RISK_TOLERANCE;
|
|
101
|
+
}
|
|
102
|
+
return this.config;
|
|
103
|
+
}
|
|
104
|
+
// Auto-detect and load config
|
|
105
|
+
autoLoad(basePath = process.cwd()) {
|
|
106
|
+
const configFiles = [
|
|
107
|
+
'aura.config.json',
|
|
108
|
+
'aura.config.yaml',
|
|
109
|
+
'aura.config.yml',
|
|
110
|
+
'.aurarc.json',
|
|
111
|
+
'.aurarc'
|
|
112
|
+
];
|
|
113
|
+
for (const file of configFiles) {
|
|
114
|
+
const fullPath = join(basePath, file);
|
|
115
|
+
if (existsSync(fullPath)) {
|
|
116
|
+
console.log(`[Config] Loading from ${file}`);
|
|
117
|
+
this.loadFromFile(fullPath);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Override with env vars
|
|
122
|
+
this.loadFromEnv();
|
|
123
|
+
return this.config;
|
|
124
|
+
}
|
|
125
|
+
getConfig() {
|
|
126
|
+
return this.config;
|
|
127
|
+
}
|
|
128
|
+
// Validate configuration
|
|
129
|
+
validate() {
|
|
130
|
+
const errors = [];
|
|
131
|
+
if (!this.config.server?.port) {
|
|
132
|
+
errors.push('server.port is required');
|
|
133
|
+
}
|
|
134
|
+
if (this.config.server.port < 1 || this.config.server.port > 65535) {
|
|
135
|
+
errors.push('server.port must be between 1 and 65535');
|
|
136
|
+
}
|
|
137
|
+
if (!this.config.modules || this.config.modules.length === 0) {
|
|
138
|
+
errors.push('At least one module must be configured');
|
|
139
|
+
}
|
|
140
|
+
if (!this.config.policy?.critical_assets) {
|
|
141
|
+
errors.push('policy.critical_assets is required');
|
|
142
|
+
}
|
|
143
|
+
return errors;
|
|
144
|
+
}
|
|
145
|
+
parseSimpleYaml(content) {
|
|
146
|
+
// Very basic YAML parser for simple configs
|
|
147
|
+
const result = {};
|
|
148
|
+
const lines = content.split('\n');
|
|
149
|
+
let currentSection = result;
|
|
150
|
+
let sectionStack = [result];
|
|
151
|
+
let indentStack = [0];
|
|
152
|
+
for (const line of lines) {
|
|
153
|
+
if (line.trim() === '' || line.trim().startsWith('#'))
|
|
154
|
+
continue;
|
|
155
|
+
const indent = line.search(/\S/);
|
|
156
|
+
const trimmed = line.trim();
|
|
157
|
+
// Handle section end
|
|
158
|
+
while (indent <= indentStack[indentStack.length - 1] && indentStack.length > 1) {
|
|
159
|
+
sectionStack.pop();
|
|
160
|
+
indentStack.pop();
|
|
161
|
+
currentSection = sectionStack[sectionStack.length - 1];
|
|
162
|
+
}
|
|
163
|
+
if (trimmed.endsWith(':')) {
|
|
164
|
+
// New section
|
|
165
|
+
const key = trimmed.slice(0, -1);
|
|
166
|
+
currentSection[key] = {};
|
|
167
|
+
sectionStack.push(currentSection[key]);
|
|
168
|
+
indentStack.push(indent);
|
|
169
|
+
currentSection = currentSection[key];
|
|
170
|
+
}
|
|
171
|
+
else if (trimmed.includes(':')) {
|
|
172
|
+
// Key-value pair
|
|
173
|
+
const [key, ...valueParts] = trimmed.split(':');
|
|
174
|
+
let value = valueParts.join(':').trim();
|
|
175
|
+
// Parse value type
|
|
176
|
+
if (value === 'true')
|
|
177
|
+
value = true;
|
|
178
|
+
else if (value === 'false')
|
|
179
|
+
value = false;
|
|
180
|
+
else if (!isNaN(Number(value)))
|
|
181
|
+
value = Number(value);
|
|
182
|
+
else if (value.startsWith('[')) {
|
|
183
|
+
// Simple array
|
|
184
|
+
value = value.slice(1, -1).split(',').map(v => v.trim().replace(/['"]/g, ''));
|
|
185
|
+
}
|
|
186
|
+
currentSection[key.trim()] = value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
mergeConfig(base, override) {
|
|
192
|
+
return {
|
|
193
|
+
version: override.version || base.version,
|
|
194
|
+
name: override.name || base.name,
|
|
195
|
+
description: override.description || base.description,
|
|
196
|
+
server: { ...base.server, ...override.server },
|
|
197
|
+
webhook: base.webhook && override.webhook
|
|
198
|
+
? { ...base.webhook, ...override.webhook }
|
|
199
|
+
: (override.webhook || base.webhook),
|
|
200
|
+
visualizer: base.visualizer && override.visualizer
|
|
201
|
+
? { ...base.visualizer, ...override.visualizer }
|
|
202
|
+
: (override.visualizer || base.visualizer),
|
|
203
|
+
modules: override.modules || base.modules,
|
|
204
|
+
policy: { ...base.policy, ...override.policy },
|
|
205
|
+
integrations: override.integrations
|
|
206
|
+
? { ...base.integrations, ...override.integrations }
|
|
207
|
+
: base.integrations
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Export singleton loader
|
|
212
|
+
export const configLoader = new ConfigLoader();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AuditorInput, AuditorOutput } from '../types/events.js';
|
|
2
|
+
export interface GitHubConfig {
|
|
3
|
+
token: string;
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PullRequest {
|
|
7
|
+
number: number;
|
|
8
|
+
title: string;
|
|
9
|
+
body: string;
|
|
10
|
+
head: {
|
|
11
|
+
sha: string;
|
|
12
|
+
ref: string;
|
|
13
|
+
};
|
|
14
|
+
base: {
|
|
15
|
+
ref: string;
|
|
16
|
+
};
|
|
17
|
+
user: {
|
|
18
|
+
login: string;
|
|
19
|
+
};
|
|
20
|
+
changed_files: number;
|
|
21
|
+
additions: number;
|
|
22
|
+
deletions: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ChangedFile {
|
|
25
|
+
filename: string;
|
|
26
|
+
status: string;
|
|
27
|
+
additions: number;
|
|
28
|
+
deletions: number;
|
|
29
|
+
patch?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class GitHubIntegration {
|
|
32
|
+
private token;
|
|
33
|
+
private apiUrl;
|
|
34
|
+
constructor(config: GitHubConfig);
|
|
35
|
+
private fetch;
|
|
36
|
+
getPullRequest(owner: string, repo: string, prNumber: number): Promise<PullRequest>;
|
|
37
|
+
getPullRequestFiles(owner: string, repo: string, prNumber: number): Promise<ChangedFile[]>;
|
|
38
|
+
getPullRequestDiff(owner: string, repo: string, prNumber: number): Promise<string>;
|
|
39
|
+
createAuditInput(owner: string, repo: string, prNumber: number, criticalAssets?: string[]): Promise<AuditorInput>;
|
|
40
|
+
createCheckRun(owner: string, repo: string, headSha: string, output: AuditorOutput): Promise<void>;
|
|
41
|
+
createPRComment(owner: string, repo: string, prNumber: number, output: AuditorOutput): Promise<void>;
|
|
42
|
+
private formatCheckSummary;
|
|
43
|
+
private formatAnnotations;
|
|
44
|
+
private formatCommentBody;
|
|
45
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// GitHub Integration - Fetch PR details, diffs, and create check runs
|
|
2
|
+
// Requires: GITHUB_TOKEN environment variable
|
|
3
|
+
export class GitHubIntegration {
|
|
4
|
+
token;
|
|
5
|
+
apiUrl;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.token = config.token;
|
|
8
|
+
this.apiUrl = config.apiUrl || 'https://api.github.com';
|
|
9
|
+
}
|
|
10
|
+
async fetch(endpoint, options = {}) {
|
|
11
|
+
const res = await fetch(`${this.apiUrl}${endpoint}`, {
|
|
12
|
+
...options,
|
|
13
|
+
headers: {
|
|
14
|
+
'Authorization': `Bearer ${this.token}`,
|
|
15
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
16
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
17
|
+
...options.headers
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
throw new Error(`GitHub API error: ${res.status} ${res.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
return res.json();
|
|
24
|
+
}
|
|
25
|
+
// Get pull request details
|
|
26
|
+
async getPullRequest(owner, repo, prNumber) {
|
|
27
|
+
return this.fetch(`/repos/${owner}/${repo}/pulls/${prNumber}`);
|
|
28
|
+
}
|
|
29
|
+
// Get files changed in PR
|
|
30
|
+
async getPullRequestFiles(owner, repo, prNumber) {
|
|
31
|
+
return this.fetch(`/repos/${owner}/${repo}/pulls/${prNumber}/files`);
|
|
32
|
+
}
|
|
33
|
+
// Get full diff
|
|
34
|
+
async getPullRequestDiff(owner, repo, prNumber) {
|
|
35
|
+
const res = await fetch(`${this.apiUrl}/repos/${owner}/${repo}/pulls/${prNumber}`, {
|
|
36
|
+
headers: {
|
|
37
|
+
'Authorization': `Bearer ${this.token}`,
|
|
38
|
+
'Accept': 'application/vnd.github.v3.diff'
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
throw new Error(`GitHub API error: ${res.status}`);
|
|
43
|
+
}
|
|
44
|
+
return res.text();
|
|
45
|
+
}
|
|
46
|
+
// Create audit input from PR
|
|
47
|
+
async createAuditInput(owner, repo, prNumber, criticalAssets = ['auth', 'billing', 'database', 'secrets', 'infra']) {
|
|
48
|
+
const [pr, files, diff] = await Promise.all([
|
|
49
|
+
this.getPullRequest(owner, repo, prNumber),
|
|
50
|
+
this.getPullRequestFiles(owner, repo, prNumber),
|
|
51
|
+
this.getPullRequestDiff(owner, repo, prNumber)
|
|
52
|
+
]);
|
|
53
|
+
const isProd = pr.base.ref === 'main' || pr.base.ref === 'master';
|
|
54
|
+
return {
|
|
55
|
+
change_event: {
|
|
56
|
+
id: `github-${owner}-${repo}-pr-${prNumber}`,
|
|
57
|
+
type: 'pull_request',
|
|
58
|
+
environment: isProd ? 'prod' : 'staging',
|
|
59
|
+
repo: `${owner}/${repo}`,
|
|
60
|
+
commit: pr.head.sha,
|
|
61
|
+
files_changed: files.map(f => f.filename),
|
|
62
|
+
diff: diff
|
|
63
|
+
},
|
|
64
|
+
evidence_bundle: {
|
|
65
|
+
// Can be populated by scanner integrations
|
|
66
|
+
},
|
|
67
|
+
policy_context: {
|
|
68
|
+
critical_assets: criticalAssets,
|
|
69
|
+
risk_tolerance: isProd ? 'low' : 'medium'
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Create a check run for the PR
|
|
74
|
+
async createCheckRun(owner, repo, headSha, output) {
|
|
75
|
+
const criticalCount = output.events.filter(e => e.payload.severity === 'critical').length;
|
|
76
|
+
const highCount = output.events.filter(e => e.payload.severity === 'high').length;
|
|
77
|
+
const conclusion = criticalCount > 0 ? 'failure' :
|
|
78
|
+
highCount > 0 ? 'neutral' : 'success';
|
|
79
|
+
const summary = this.formatCheckSummary(output);
|
|
80
|
+
const annotations = this.formatAnnotations(output);
|
|
81
|
+
await this.fetch(`/repos/${owner}/${repo}/check-runs`, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'Content-Type': 'application/json' },
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
name: 'aurasecurity Audit',
|
|
86
|
+
head_sha: headSha,
|
|
87
|
+
status: 'completed',
|
|
88
|
+
conclusion,
|
|
89
|
+
output: {
|
|
90
|
+
title: `Security Audit: ${output.agent_state.toUpperCase()}`,
|
|
91
|
+
summary,
|
|
92
|
+
annotations: annotations.slice(0, 50) // GitHub limit
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Create a PR comment with audit results
|
|
98
|
+
async createPRComment(owner, repo, prNumber, output) {
|
|
99
|
+
const body = this.formatCommentBody(output);
|
|
100
|
+
await this.fetch(`/repos/${owner}/${repo}/issues/${prNumber}/comments`, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: { 'Content-Type': 'application/json' },
|
|
103
|
+
body: JSON.stringify({ body })
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
formatCheckSummary(output) {
|
|
107
|
+
const counts = {
|
|
108
|
+
critical: output.events.filter(e => e.payload.severity === 'critical').length,
|
|
109
|
+
high: output.events.filter(e => e.payload.severity === 'high').length,
|
|
110
|
+
medium: output.events.filter(e => e.payload.severity === 'medium').length,
|
|
111
|
+
low: output.events.filter(e => e.payload.severity === 'low').length
|
|
112
|
+
};
|
|
113
|
+
return `## Security Audit Results
|
|
114
|
+
|
|
115
|
+
**Agent State:** ${output.agent_state.toUpperCase()}
|
|
116
|
+
|
|
117
|
+
### Findings Summary
|
|
118
|
+
| Severity | Count |
|
|
119
|
+
|----------|-------|
|
|
120
|
+
| Critical | ${counts.critical} |
|
|
121
|
+
| High | ${counts.high} |
|
|
122
|
+
| Medium | ${counts.medium} |
|
|
123
|
+
| Low | ${counts.low} |
|
|
124
|
+
|
|
125
|
+
### Details
|
|
126
|
+
${output.events.filter(e => e.event_type !== 'analysis_started').map(e => `
|
|
127
|
+
#### ${e.payload.severity.toUpperCase()}: ${e.payload.claim}
|
|
128
|
+
- **Confidence:** ${(e.payload.confidence * 100).toFixed(0)}%
|
|
129
|
+
- **Affected:** ${e.payload.affected_assets.join(', ') || 'N/A'}
|
|
130
|
+
- **Attack Path:**
|
|
131
|
+
${e.payload.attack_path.map(p => ` 1. ${p}`).join('\n')}
|
|
132
|
+
`).join('\n')}
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
formatAnnotations(output) {
|
|
136
|
+
const annotations = [];
|
|
137
|
+
for (const event of output.events) {
|
|
138
|
+
if (event.event_type === 'analysis_started')
|
|
139
|
+
continue;
|
|
140
|
+
for (const ref of event.payload.evidence_refs) {
|
|
141
|
+
if (ref.type === 'diff') {
|
|
142
|
+
const level = event.payload.severity === 'critical' ? 'failure' :
|
|
143
|
+
event.payload.severity === 'high' ? 'warning' : 'notice';
|
|
144
|
+
annotations.push({
|
|
145
|
+
path: ref.pointer.includes('/') ? ref.pointer : 'unknown',
|
|
146
|
+
start_line: 1,
|
|
147
|
+
end_line: 1,
|
|
148
|
+
annotation_level: level,
|
|
149
|
+
message: event.payload.claim,
|
|
150
|
+
title: `${event.payload.severity.toUpperCase()}: Security Finding`
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return annotations;
|
|
156
|
+
}
|
|
157
|
+
formatCommentBody(output) {
|
|
158
|
+
const emoji = output.agent_state === 'blocked' ? '🚨' :
|
|
159
|
+
output.agent_state === 'escalated' ? '⚠️' :
|
|
160
|
+
output.agent_state === 'conflict' ? '⚡' : '✅';
|
|
161
|
+
const counts = {
|
|
162
|
+
critical: output.events.filter(e => e.payload.severity === 'critical').length,
|
|
163
|
+
high: output.events.filter(e => e.payload.severity === 'high').length,
|
|
164
|
+
medium: output.events.filter(e => e.payload.severity === 'medium').length,
|
|
165
|
+
low: output.events.filter(e => e.payload.severity === 'low').length
|
|
166
|
+
};
|
|
167
|
+
let body = `## ${emoji} aurasecurity Audit
|
|
168
|
+
|
|
169
|
+
**Status:** ${output.agent_state.toUpperCase()}
|
|
170
|
+
|
|
171
|
+
| Critical | High | Medium | Low |
|
|
172
|
+
|:--------:|:----:|:------:|:---:|
|
|
173
|
+
| ${counts.critical} | ${counts.high} | ${counts.medium} | ${counts.low} |
|
|
174
|
+
|
|
175
|
+
`;
|
|
176
|
+
if (output.events.length > 1) {
|
|
177
|
+
body += `### Findings\n\n`;
|
|
178
|
+
for (const event of output.events) {
|
|
179
|
+
if (event.event_type === 'analysis_started')
|
|
180
|
+
continue;
|
|
181
|
+
const sevEmoji = event.payload.severity === 'critical' ? '🔴' :
|
|
182
|
+
event.payload.severity === 'high' ? '🟠' :
|
|
183
|
+
event.payload.severity === 'medium' ? '🟡' : '🟢';
|
|
184
|
+
body += `<details>
|
|
185
|
+
<summary>${sevEmoji} <strong>${event.payload.severity.toUpperCase()}</strong>: ${event.payload.claim}</summary>
|
|
186
|
+
|
|
187
|
+
**Confidence:** ${(event.payload.confidence * 100).toFixed(0)}%
|
|
188
|
+
**Affected:** ${event.payload.affected_assets.join(', ') || 'N/A'}
|
|
189
|
+
|
|
190
|
+
**Attack Path:**
|
|
191
|
+
${event.payload.attack_path.map((p, i) => `${i + 1}. ${p}`).join('\n')}
|
|
192
|
+
|
|
193
|
+
</details>
|
|
194
|
+
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
body += `\n---\n*Powered by aurasecurity*`;
|
|
199
|
+
return body;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AuditorInput, AuditorOutput } from '../types/events.js';
|
|
2
|
+
export interface GitLabConfig {
|
|
3
|
+
token: string;
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface MergeRequest {
|
|
7
|
+
iid: number;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
source_branch: string;
|
|
11
|
+
target_branch: string;
|
|
12
|
+
sha: string;
|
|
13
|
+
author: {
|
|
14
|
+
username: string;
|
|
15
|
+
};
|
|
16
|
+
changes_count: string;
|
|
17
|
+
}
|
|
18
|
+
export interface MergeRequestChange {
|
|
19
|
+
old_path: string;
|
|
20
|
+
new_path: string;
|
|
21
|
+
diff: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class GitLabIntegration {
|
|
24
|
+
private token;
|
|
25
|
+
private apiUrl;
|
|
26
|
+
constructor(config: GitLabConfig);
|
|
27
|
+
private fetch;
|
|
28
|
+
getMergeRequest(projectId: string | number, mrIid: number): Promise<MergeRequest>;
|
|
29
|
+
getMergeRequestChanges(projectId: string | number, mrIid: number): Promise<{
|
|
30
|
+
changes: MergeRequestChange[];
|
|
31
|
+
}>;
|
|
32
|
+
createAuditInput(projectId: string | number, mrIid: number, criticalAssets?: string[]): Promise<AuditorInput>;
|
|
33
|
+
createMRComment(projectId: string | number, mrIid: number, output: AuditorOutput): Promise<void>;
|
|
34
|
+
updateCommitStatus(projectId: string | number, sha: string, output: AuditorOutput): Promise<void>;
|
|
35
|
+
private formatCommentBody;
|
|
36
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// GitLab Integration - Fetch MR details and create pipeline comments
|
|
2
|
+
// Requires: GITLAB_TOKEN environment variable
|
|
3
|
+
export class GitLabIntegration {
|
|
4
|
+
token;
|
|
5
|
+
apiUrl;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.token = config.token;
|
|
8
|
+
this.apiUrl = config.apiUrl || 'https://gitlab.com/api/v4';
|
|
9
|
+
}
|
|
10
|
+
async fetch(endpoint, options = {}) {
|
|
11
|
+
const res = await fetch(`${this.apiUrl}${endpoint}`, {
|
|
12
|
+
...options,
|
|
13
|
+
headers: {
|
|
14
|
+
'PRIVATE-TOKEN': this.token,
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
...options.headers
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`GitLab API error: ${res.status} ${res.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
24
|
+
// Get merge request details
|
|
25
|
+
async getMergeRequest(projectId, mrIid) {
|
|
26
|
+
return this.fetch(`/projects/${encodeURIComponent(projectId)}/merge_requests/${mrIid}`);
|
|
27
|
+
}
|
|
28
|
+
// Get MR changes/diff
|
|
29
|
+
async getMergeRequestChanges(projectId, mrIid) {
|
|
30
|
+
return this.fetch(`/projects/${encodeURIComponent(projectId)}/merge_requests/${mrIid}/changes`);
|
|
31
|
+
}
|
|
32
|
+
// Create audit input from MR
|
|
33
|
+
async createAuditInput(projectId, mrIid, criticalAssets = ['auth', 'billing', 'database', 'secrets', 'infra']) {
|
|
34
|
+
const [mr, changes] = await Promise.all([
|
|
35
|
+
this.getMergeRequest(projectId, mrIid),
|
|
36
|
+
this.getMergeRequestChanges(projectId, mrIid)
|
|
37
|
+
]);
|
|
38
|
+
const isProd = mr.target_branch === 'main' || mr.target_branch === 'master';
|
|
39
|
+
const diff = changes.changes.map(c => c.diff).join('\n');
|
|
40
|
+
return {
|
|
41
|
+
change_event: {
|
|
42
|
+
id: `gitlab-${projectId}-mr-${mrIid}`,
|
|
43
|
+
type: 'pull_request',
|
|
44
|
+
environment: isProd ? 'prod' : 'staging',
|
|
45
|
+
repo: String(projectId),
|
|
46
|
+
commit: mr.sha,
|
|
47
|
+
files_changed: changes.changes.map(c => c.new_path),
|
|
48
|
+
diff
|
|
49
|
+
},
|
|
50
|
+
evidence_bundle: {},
|
|
51
|
+
policy_context: {
|
|
52
|
+
critical_assets: criticalAssets,
|
|
53
|
+
risk_tolerance: isProd ? 'low' : 'medium'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Create MR comment with audit results
|
|
58
|
+
async createMRComment(projectId, mrIid, output) {
|
|
59
|
+
const body = this.formatCommentBody(output);
|
|
60
|
+
await this.fetch(`/projects/${encodeURIComponent(projectId)}/merge_requests/${mrIid}/notes`, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
body: JSON.stringify({ body })
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Update pipeline status
|
|
66
|
+
async updateCommitStatus(projectId, sha, output) {
|
|
67
|
+
const criticalCount = output.events.filter(e => e.payload.severity === 'critical').length;
|
|
68
|
+
const highCount = output.events.filter(e => e.payload.severity === 'high').length;
|
|
69
|
+
const state = criticalCount > 0 ? 'failed' :
|
|
70
|
+
highCount > 0 ? 'failed' : 'success';
|
|
71
|
+
await this.fetch(`/projects/${encodeURIComponent(projectId)}/statuses/${sha}`, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
state,
|
|
75
|
+
name: 'aurasecurity Audit',
|
|
76
|
+
description: `${output.agent_state.toUpperCase()} - ${criticalCount} critical, ${highCount} high`,
|
|
77
|
+
context: 'security/aura-audit'
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
formatCommentBody(output) {
|
|
82
|
+
const emoji = output.agent_state === 'blocked' ? '🚨' :
|
|
83
|
+
output.agent_state === 'escalated' ? '⚠️' : '✅';
|
|
84
|
+
const counts = {
|
|
85
|
+
critical: output.events.filter(e => e.payload.severity === 'critical').length,
|
|
86
|
+
high: output.events.filter(e => e.payload.severity === 'high').length,
|
|
87
|
+
medium: output.events.filter(e => e.payload.severity === 'medium').length,
|
|
88
|
+
low: output.events.filter(e => e.payload.severity === 'low').length
|
|
89
|
+
};
|
|
90
|
+
let body = `## ${emoji} aurasecurity Audit
|
|
91
|
+
|
|
92
|
+
**Status:** \`${output.agent_state.toUpperCase()}\`
|
|
93
|
+
|
|
94
|
+
| Critical | High | Medium | Low |
|
|
95
|
+
|:--------:|:----:|:------:|:---:|
|
|
96
|
+
| ${counts.critical} | ${counts.high} | ${counts.medium} | ${counts.low} |
|
|
97
|
+
|
|
98
|
+
`;
|
|
99
|
+
for (const event of output.events) {
|
|
100
|
+
if (event.event_type === 'analysis_started')
|
|
101
|
+
continue;
|
|
102
|
+
body += `### ${event.payload.severity.toUpperCase()}: ${event.payload.claim}
|
|
103
|
+
- Confidence: ${(event.payload.confidence * 100).toFixed(0)}%
|
|
104
|
+
- Affected: ${event.payload.affected_assets.join(', ') || 'N/A'}
|
|
105
|
+
|
|
106
|
+
`;
|
|
107
|
+
}
|
|
108
|
+
return body;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { WebhookServer, WebhookHandler } from './webhook.js';
|
|
2
|
+
export { GitHubIntegration } from './github.js';
|
|
3
|
+
export { GitLabIntegration } from './gitlab.js';
|
|
4
|
+
export { ScannerParser, SnykParser, TrivyParser, SemgrepParser } from './scanners.js';
|
|
5
|
+
export { ConfigLoader, AuditorConfig } from './config.js';
|
|
6
|
+
export { LocalScanner, quickLocalScan } from './local-scanner.js';
|
|
7
|
+
export type { LocalScanConfig, LocalScanResult, SecretFinding, PackageFinding, SastFinding, GitInfo, EnvFileFinding, SystemInfo, DiscoveredService, DiscoveredModule } from './local-scanner.js';
|
|
8
|
+
export { NotificationService, createNotificationFromAudit } from './notifications.js';
|
|
9
|
+
export { auraScan, getAuraState, getAvailableAgents, orchestrator } from './aura-scanner.js';
|
|
10
|
+
export type { AuraScanConfig, AuraScanResult } from './aura-scanner.js';
|
|
11
|
+
export type { NotificationConfig, NotificationPayload } from './notifications.js';
|