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
|
+
/**
|
|
2
|
+
* SLOP Protocol Scanner
|
|
3
|
+
*
|
|
4
|
+
* Wrapper that uses the SLOP Protocol multi-agent architecture
|
|
5
|
+
* while maintaining backward compatibility with the existing scanner interface.
|
|
6
|
+
*/
|
|
7
|
+
import { orchestrator, OrchestratorResult } from '../orchestrator/index.js';
|
|
8
|
+
import { ZoneFinding } from '../zones/types.js';
|
|
9
|
+
import type { LocalScanResult } from './local-scanner.js';
|
|
10
|
+
export interface SlopScanConfig {
|
|
11
|
+
targetPath: string;
|
|
12
|
+
fullScan?: boolean;
|
|
13
|
+
enableScannerZone?: boolean;
|
|
14
|
+
enablePolicyZone?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface SlopScanResult {
|
|
17
|
+
legacy: LocalScanResult;
|
|
18
|
+
slop: {
|
|
19
|
+
zones: Array<{
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
color: string;
|
|
24
|
+
status: string;
|
|
25
|
+
findingCount: number;
|
|
26
|
+
duration: number;
|
|
27
|
+
}>;
|
|
28
|
+
agents: Array<{
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
role: string;
|
|
32
|
+
status: string;
|
|
33
|
+
findingCount: number;
|
|
34
|
+
duration: number;
|
|
35
|
+
}>;
|
|
36
|
+
findings: ZoneFinding[];
|
|
37
|
+
summary: OrchestratorResult['summary'];
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Run a security scan using the SLOP Protocol architecture
|
|
42
|
+
*/
|
|
43
|
+
export declare function slopScan(config: SlopScanConfig): Promise<SlopScanResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Get orchestrator state for visualization
|
|
46
|
+
*/
|
|
47
|
+
export declare function getSlopState(): {
|
|
48
|
+
zones: Array<{
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
type: string;
|
|
52
|
+
color: string;
|
|
53
|
+
status: import("../zones/types.js").ZoneStatus;
|
|
54
|
+
agentCount: number;
|
|
55
|
+
findingCount: number;
|
|
56
|
+
}>;
|
|
57
|
+
agents: Array<{
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
role: string;
|
|
61
|
+
zoneId: string | null;
|
|
62
|
+
status: string;
|
|
63
|
+
}>;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Get available agents
|
|
67
|
+
*/
|
|
68
|
+
export declare function getAvailableAgents(): Promise<import("../agents/types.js").Agent[]>;
|
|
69
|
+
export { orchestrator };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SLOP Protocol Scanner
|
|
3
|
+
*
|
|
4
|
+
* Wrapper that uses the SLOP Protocol multi-agent architecture
|
|
5
|
+
* while maintaining backward compatibility with the existing scanner interface.
|
|
6
|
+
*/
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import { orchestrator } from '../orchestrator/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* Run a security scan using the SLOP Protocol architecture
|
|
11
|
+
*/
|
|
12
|
+
export async function slopScan(config) {
|
|
13
|
+
console.log('[SLOP] Starting SLOP Protocol scan...');
|
|
14
|
+
console.log(`[SLOP] Target: ${config.targetPath}`);
|
|
15
|
+
// Run orchestrator
|
|
16
|
+
const result = config.fullScan !== false
|
|
17
|
+
? await orchestrator.fullScan(config.targetPath)
|
|
18
|
+
: await orchestrator.quickScan(config.targetPath);
|
|
19
|
+
console.log(`[SLOP] Scan complete in ${result.duration}ms`);
|
|
20
|
+
console.log(`[SLOP] Agents used: ${result.summary.agentsUsed.join(', ')}`);
|
|
21
|
+
console.log(`[SLOP] Total findings: ${result.summary.totalFindings}`);
|
|
22
|
+
// Convert to legacy format
|
|
23
|
+
const legacy = convertToLegacyFormat(result);
|
|
24
|
+
// Build SLOP format with zone info
|
|
25
|
+
const zones = Array.from(result.zoneResults.entries()).map(([zoneId, zoneResult]) => ({
|
|
26
|
+
id: zoneId,
|
|
27
|
+
name: zoneResult.zoneName,
|
|
28
|
+
type: zoneResult.zoneType,
|
|
29
|
+
color: getZoneColor(zoneResult.zoneType),
|
|
30
|
+
status: zoneResult.status,
|
|
31
|
+
findingCount: zoneResult.findings.length,
|
|
32
|
+
duration: zoneResult.duration,
|
|
33
|
+
}));
|
|
34
|
+
const agents = Array.from(result.zoneResults.values()).flatMap((zoneResult) => zoneResult.agentResults.map((agentResult) => ({
|
|
35
|
+
id: agentResult.agentId,
|
|
36
|
+
name: agentResult.agentName,
|
|
37
|
+
role: getAgentRole(agentResult.agentId),
|
|
38
|
+
status: agentResult.status,
|
|
39
|
+
findingCount: agentResult.findings.length,
|
|
40
|
+
duration: agentResult.duration,
|
|
41
|
+
})));
|
|
42
|
+
return {
|
|
43
|
+
legacy,
|
|
44
|
+
slop: {
|
|
45
|
+
zones,
|
|
46
|
+
agents,
|
|
47
|
+
findings: result.findings,
|
|
48
|
+
summary: result.summary,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Convert orchestrator result to legacy LocalScanResult format
|
|
54
|
+
*/
|
|
55
|
+
function convertToLegacyFormat(result) {
|
|
56
|
+
const secrets = [];
|
|
57
|
+
const packages = [];
|
|
58
|
+
const sastFindings = [];
|
|
59
|
+
const iacFindings = [];
|
|
60
|
+
for (const finding of result.findings) {
|
|
61
|
+
if (finding.type === 'secret') {
|
|
62
|
+
secrets.push({
|
|
63
|
+
file: finding.file || '',
|
|
64
|
+
line: finding.line || 0,
|
|
65
|
+
type: finding.title,
|
|
66
|
+
snippet: finding.metadata?.match || '***',
|
|
67
|
+
severity: finding.severity,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else if (finding.type === 'vulnerability') {
|
|
71
|
+
packages.push({
|
|
72
|
+
name: finding.metadata?.package || finding.title,
|
|
73
|
+
version: finding.metadata?.installedVersion || finding.metadata?.version || 'unknown',
|
|
74
|
+
vulnerabilities: 1,
|
|
75
|
+
severity: finding.severity,
|
|
76
|
+
vulnId: finding.metadata?.vulnerabilityId || undefined,
|
|
77
|
+
title: finding.title,
|
|
78
|
+
fixedVersion: finding.metadata?.fixedVersion || finding.metadata?.fixVersions?.[0],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else if (finding.type === 'policy_violation') {
|
|
82
|
+
// Map policy violations to SAST findings
|
|
83
|
+
sastFindings.push({
|
|
84
|
+
file: finding.file || '',
|
|
85
|
+
line: finding.line || 0,
|
|
86
|
+
rule: finding.title,
|
|
87
|
+
message: finding.description,
|
|
88
|
+
severity: finding.severity,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
path: result.targetPath,
|
|
94
|
+
timestamp: new Date().toISOString(),
|
|
95
|
+
secrets,
|
|
96
|
+
packages,
|
|
97
|
+
sastFindings,
|
|
98
|
+
iacFindings,
|
|
99
|
+
dockerfileFindings: [],
|
|
100
|
+
gitInfo: null,
|
|
101
|
+
envFiles: [],
|
|
102
|
+
systemInfo: {
|
|
103
|
+
platform: process.platform,
|
|
104
|
+
hostname: os.hostname(),
|
|
105
|
+
user: os.userInfo().username,
|
|
106
|
+
nodeVersion: process.version,
|
|
107
|
+
cwd: process.cwd(),
|
|
108
|
+
},
|
|
109
|
+
discoveredServices: [],
|
|
110
|
+
discoveredModules: [],
|
|
111
|
+
toolsUsed: result.summary.agentsUsed,
|
|
112
|
+
languagesDetected: [],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function getZoneColor(zoneType) {
|
|
116
|
+
switch (zoneType) {
|
|
117
|
+
case 'scanner':
|
|
118
|
+
return '#22c55e'; // Green
|
|
119
|
+
case 'policy':
|
|
120
|
+
return '#ef4444'; // Red
|
|
121
|
+
case 'reporting':
|
|
122
|
+
return '#3b82f6'; // Blue
|
|
123
|
+
default:
|
|
124
|
+
return '#888888';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function getAgentRole(agentId) {
|
|
128
|
+
const scannerAgents = ['gitleaks', 'trivy', 'semgrep', 'grype', 'npm-audit'];
|
|
129
|
+
const policyAgents = ['policy-evaluator', 'validator'];
|
|
130
|
+
const reporterAgents = ['sarif-reporter'];
|
|
131
|
+
const notifierAgents = ['slack-notifier', 'discord-notifier'];
|
|
132
|
+
if (scannerAgents.includes(agentId))
|
|
133
|
+
return 'scanner';
|
|
134
|
+
if (policyAgents.includes(agentId))
|
|
135
|
+
return 'policy';
|
|
136
|
+
if (reporterAgents.includes(agentId))
|
|
137
|
+
return 'reporter';
|
|
138
|
+
if (notifierAgents.includes(agentId))
|
|
139
|
+
return 'notifier';
|
|
140
|
+
return 'unknown';
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get orchestrator state for visualization
|
|
144
|
+
*/
|
|
145
|
+
export function getSlopState() {
|
|
146
|
+
return orchestrator.getState();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get available agents
|
|
150
|
+
*/
|
|
151
|
+
export async function getAvailableAgents() {
|
|
152
|
+
return orchestrator.getAvailableAgents();
|
|
153
|
+
}
|
|
154
|
+
// Export orchestrator for direct access
|
|
155
|
+
export { orchestrator };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AuditorInput } from '../types/events.js';
|
|
2
|
+
export interface WebhookConfig {
|
|
3
|
+
port: number;
|
|
4
|
+
secret?: string;
|
|
5
|
+
allowedSources?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface WebhookEvent {
|
|
8
|
+
source: 'github' | 'gitlab' | 'jenkins' | 'custom';
|
|
9
|
+
type: string;
|
|
10
|
+
payload: Record<string, unknown>;
|
|
11
|
+
timestamp: string;
|
|
12
|
+
signature?: string;
|
|
13
|
+
}
|
|
14
|
+
export type WebhookHandler = (event: WebhookEvent) => Promise<AuditorInput | null>;
|
|
15
|
+
export declare class WebhookServer {
|
|
16
|
+
private server;
|
|
17
|
+
private config;
|
|
18
|
+
private handlers;
|
|
19
|
+
private onAuditRequest;
|
|
20
|
+
constructor(config: WebhookConfig);
|
|
21
|
+
registerHandler(eventType: string, handler: WebhookHandler): void;
|
|
22
|
+
onAudit(callback: (input: AuditorInput) => Promise<void>): void;
|
|
23
|
+
private handleRequest;
|
|
24
|
+
private detectSource;
|
|
25
|
+
private getSignature;
|
|
26
|
+
private getEventType;
|
|
27
|
+
private verifySignature;
|
|
28
|
+
private readBody;
|
|
29
|
+
start(): Promise<void>;
|
|
30
|
+
stop(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export declare const defaultHandlers: {
|
|
33
|
+
'github:pull_request': (event: WebhookEvent) => Promise<AuditorInput | null>;
|
|
34
|
+
'github:push': (event: WebhookEvent) => Promise<AuditorInput | null>;
|
|
35
|
+
'gitlab:merge_request': (event: WebhookEvent) => Promise<AuditorInput | null>;
|
|
36
|
+
'jenkins:build': (event: WebhookEvent) => Promise<AuditorInput | null>;
|
|
37
|
+
};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// Webhook Server - Receive events from external systems
|
|
2
|
+
// Supports GitHub, GitLab, Jenkins, and custom webhooks
|
|
3
|
+
import { createServer } from 'http';
|
|
4
|
+
import { createHmac } from 'crypto';
|
|
5
|
+
export class WebhookServer {
|
|
6
|
+
server = null;
|
|
7
|
+
config;
|
|
8
|
+
handlers = new Map();
|
|
9
|
+
onAuditRequest = null;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
// Register handler for specific event types
|
|
14
|
+
registerHandler(eventType, handler) {
|
|
15
|
+
this.handlers.set(eventType, handler);
|
|
16
|
+
}
|
|
17
|
+
// Set callback for when audit should be triggered
|
|
18
|
+
onAudit(callback) {
|
|
19
|
+
this.onAuditRequest = callback;
|
|
20
|
+
}
|
|
21
|
+
async handleRequest(req, res) {
|
|
22
|
+
// CORS
|
|
23
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
24
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
25
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Hub-Signature-256, X-GitLab-Token');
|
|
26
|
+
res.setHeader('Content-Type', 'application/json');
|
|
27
|
+
if (req.method === 'OPTIONS') {
|
|
28
|
+
res.statusCode = 204;
|
|
29
|
+
res.end();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (req.method !== 'POST') {
|
|
33
|
+
res.statusCode = 405;
|
|
34
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
|
|
38
|
+
const path = url.pathname;
|
|
39
|
+
try {
|
|
40
|
+
const body = await this.readBody(req);
|
|
41
|
+
const payload = JSON.parse(body);
|
|
42
|
+
// Detect source and verify signature
|
|
43
|
+
const source = this.detectSource(req, path);
|
|
44
|
+
const signature = this.getSignature(req);
|
|
45
|
+
if (this.config.secret && !this.verifySignature(body, signature, source)) {
|
|
46
|
+
res.statusCode = 401;
|
|
47
|
+
res.end(JSON.stringify({ error: 'Invalid signature' }));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Create webhook event
|
|
51
|
+
const event = {
|
|
52
|
+
source,
|
|
53
|
+
type: this.getEventType(req, source, payload),
|
|
54
|
+
payload,
|
|
55
|
+
timestamp: new Date().toISOString(),
|
|
56
|
+
signature
|
|
57
|
+
};
|
|
58
|
+
console.log(`[Webhook] Received ${event.source}:${event.type}`);
|
|
59
|
+
// Find handler
|
|
60
|
+
const handler = this.handlers.get(`${event.source}:${event.type}`) ||
|
|
61
|
+
this.handlers.get(event.source) ||
|
|
62
|
+
this.handlers.get('*');
|
|
63
|
+
if (handler) {
|
|
64
|
+
const auditInput = await handler(event);
|
|
65
|
+
if (auditInput && this.onAuditRequest) {
|
|
66
|
+
await this.onAuditRequest(auditInput);
|
|
67
|
+
res.statusCode = 200;
|
|
68
|
+
res.end(JSON.stringify({ status: 'audit_triggered', event_id: auditInput.change_event.id }));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
res.statusCode = 200;
|
|
73
|
+
res.end(JSON.stringify({ status: 'received', processed: !!handler }));
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
console.error('[Webhook] Error:', err);
|
|
77
|
+
res.statusCode = 500;
|
|
78
|
+
res.end(JSON.stringify({ error: 'Internal error' }));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
detectSource(req, path) {
|
|
82
|
+
if (req.headers['x-github-event'] || path.includes('github'))
|
|
83
|
+
return 'github';
|
|
84
|
+
if (req.headers['x-gitlab-event'] || path.includes('gitlab'))
|
|
85
|
+
return 'gitlab';
|
|
86
|
+
if (req.headers['x-jenkins-event'] || path.includes('jenkins'))
|
|
87
|
+
return 'jenkins';
|
|
88
|
+
return 'custom';
|
|
89
|
+
}
|
|
90
|
+
getSignature(req) {
|
|
91
|
+
return req.headers['x-hub-signature-256'] ||
|
|
92
|
+
req.headers['x-gitlab-token'] ||
|
|
93
|
+
req.headers['x-signature'];
|
|
94
|
+
}
|
|
95
|
+
getEventType(req, source, payload) {
|
|
96
|
+
if (source === 'github') {
|
|
97
|
+
return req.headers['x-github-event'] || 'unknown';
|
|
98
|
+
}
|
|
99
|
+
if (source === 'gitlab') {
|
|
100
|
+
return payload.object_kind || req.headers['x-gitlab-event'] || 'unknown';
|
|
101
|
+
}
|
|
102
|
+
if (source === 'jenkins') {
|
|
103
|
+
const build = payload.build;
|
|
104
|
+
return build?.phase || 'build';
|
|
105
|
+
}
|
|
106
|
+
return payload.type || 'unknown';
|
|
107
|
+
}
|
|
108
|
+
verifySignature(body, signature, source) {
|
|
109
|
+
if (!signature || !this.config.secret)
|
|
110
|
+
return false;
|
|
111
|
+
if (source === 'github') {
|
|
112
|
+
const expected = 'sha256=' + createHmac('sha256', this.config.secret).update(body).digest('hex');
|
|
113
|
+
return signature === expected;
|
|
114
|
+
}
|
|
115
|
+
if (source === 'gitlab') {
|
|
116
|
+
return signature === this.config.secret;
|
|
117
|
+
}
|
|
118
|
+
// Generic HMAC verification
|
|
119
|
+
const expected = createHmac('sha256', this.config.secret).update(body).digest('hex');
|
|
120
|
+
return signature === expected;
|
|
121
|
+
}
|
|
122
|
+
readBody(req) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
const chunks = [];
|
|
125
|
+
req.on('data', chunk => chunks.push(chunk));
|
|
126
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString()));
|
|
127
|
+
req.on('error', reject);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async start() {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
this.server = createServer((req, res) => {
|
|
133
|
+
this.handleRequest(req, res).catch(() => {
|
|
134
|
+
res.statusCode = 500;
|
|
135
|
+
res.end(JSON.stringify({ error: 'Internal error' }));
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
this.server.on('error', reject);
|
|
139
|
+
this.server.listen(this.config.port, '0.0.0.0', () => {
|
|
140
|
+
console.log(`[Webhook] Server listening on port ${this.config.port}`);
|
|
141
|
+
resolve();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async stop() {
|
|
146
|
+
return new Promise((resolve) => {
|
|
147
|
+
if (this.server) {
|
|
148
|
+
this.server.close(() => resolve());
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
resolve();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Default handlers for common webhook types
|
|
157
|
+
export const defaultHandlers = {
|
|
158
|
+
// GitHub Pull Request
|
|
159
|
+
'github:pull_request': async (event) => {
|
|
160
|
+
const pr = event.payload.pull_request;
|
|
161
|
+
const repo = event.payload.repository;
|
|
162
|
+
const action = event.payload.action;
|
|
163
|
+
if (!['opened', 'synchronize', 'reopened'].includes(action)) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
change_event: {
|
|
168
|
+
id: `github-pr-${pr.number}`,
|
|
169
|
+
type: 'pull_request',
|
|
170
|
+
environment: pr.base?.ref === 'main' ? 'prod' : 'staging',
|
|
171
|
+
repo: repo.full_name,
|
|
172
|
+
commit: pr.head?.sha,
|
|
173
|
+
files_changed: [], // Would need separate API call
|
|
174
|
+
diff: '' // Would need separate API call
|
|
175
|
+
},
|
|
176
|
+
evidence_bundle: {},
|
|
177
|
+
policy_context: {
|
|
178
|
+
critical_assets: ['auth', 'billing', 'database', 'secrets'],
|
|
179
|
+
risk_tolerance: 'medium'
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
// GitHub Push
|
|
184
|
+
'github:push': async (event) => {
|
|
185
|
+
const repo = event.payload.repository;
|
|
186
|
+
const commits = event.payload.commits || [];
|
|
187
|
+
const ref = event.payload.ref;
|
|
188
|
+
const isProd = ref === 'refs/heads/main' || ref === 'refs/heads/master';
|
|
189
|
+
return {
|
|
190
|
+
change_event: {
|
|
191
|
+
id: `github-push-${event.payload.after}`,
|
|
192
|
+
type: isProd ? 'deploy' : 'pull_request',
|
|
193
|
+
environment: isProd ? 'prod' : 'dev',
|
|
194
|
+
repo: repo.full_name,
|
|
195
|
+
commit: event.payload.after,
|
|
196
|
+
files_changed: commits.flatMap(c => [
|
|
197
|
+
...(c.added || []),
|
|
198
|
+
...(c.modified || [])
|
|
199
|
+
]),
|
|
200
|
+
diff: commits.map(c => c.message).join('\n')
|
|
201
|
+
},
|
|
202
|
+
evidence_bundle: {},
|
|
203
|
+
policy_context: {
|
|
204
|
+
critical_assets: ['auth', 'billing', 'database', 'secrets'],
|
|
205
|
+
risk_tolerance: isProd ? 'low' : 'medium'
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
// GitLab Merge Request
|
|
210
|
+
'gitlab:merge_request': async (event) => {
|
|
211
|
+
const mr = event.payload.object_attributes;
|
|
212
|
+
const project = event.payload.project;
|
|
213
|
+
if (!['open', 'reopen', 'update'].includes(mr.action)) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
change_event: {
|
|
218
|
+
id: `gitlab-mr-${mr.iid}`,
|
|
219
|
+
type: 'pull_request',
|
|
220
|
+
environment: mr.target_branch === 'main' ? 'prod' : 'staging',
|
|
221
|
+
repo: project.path_with_namespace,
|
|
222
|
+
commit: mr.last_commit,
|
|
223
|
+
files_changed: [],
|
|
224
|
+
diff: ''
|
|
225
|
+
},
|
|
226
|
+
evidence_bundle: {},
|
|
227
|
+
policy_context: {
|
|
228
|
+
critical_assets: ['auth', 'billing', 'database', 'secrets'],
|
|
229
|
+
risk_tolerance: 'medium'
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
// Jenkins Build
|
|
234
|
+
'jenkins:build': async (event) => {
|
|
235
|
+
const build = event.payload.build || {};
|
|
236
|
+
const params = build.parameters || {};
|
|
237
|
+
const envVal = params.ENVIRONMENT;
|
|
238
|
+
const environment = (envVal === 'prod' || envVal === 'staging' || envVal === 'dev') ? envVal : 'staging';
|
|
239
|
+
return {
|
|
240
|
+
change_event: {
|
|
241
|
+
id: `jenkins-${build.number || Date.now()}`,
|
|
242
|
+
type: 'deploy',
|
|
243
|
+
environment,
|
|
244
|
+
repo: event.payload.name || 'unknown',
|
|
245
|
+
commit: build.scm?.commit || '',
|
|
246
|
+
files_changed: [],
|
|
247
|
+
diff: ''
|
|
248
|
+
},
|
|
249
|
+
evidence_bundle: {},
|
|
250
|
+
policy_context: {
|
|
251
|
+
critical_assets: ['auth', 'billing', 'database', 'secrets'],
|
|
252
|
+
risk_tolerance: 'medium'
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aura Protocol - Parallel Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates parallel execution of zones and manages data flow between them.
|
|
5
|
+
* This is the main entry point for running security scans using the Aura architecture.
|
|
6
|
+
*/
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
import { ZoneManager } from '../zones/manager.js';
|
|
9
|
+
import { ZoneConfig, ZoneResult, ZoneFinding } from '../zones/types.js';
|
|
10
|
+
import { Agent } from '../agents/types.js';
|
|
11
|
+
export interface OrchestratorConfig {
|
|
12
|
+
targetPath: string;
|
|
13
|
+
zones?: string[];
|
|
14
|
+
runPolicyZone?: boolean;
|
|
15
|
+
customZones?: ZoneConfig[];
|
|
16
|
+
}
|
|
17
|
+
export interface OrchestratorResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
duration: number;
|
|
20
|
+
targetPath: string;
|
|
21
|
+
zoneResults: Map<string, ZoneResult>;
|
|
22
|
+
findings: ZoneFinding[];
|
|
23
|
+
summary: {
|
|
24
|
+
totalFindings: number;
|
|
25
|
+
byType: Record<string, number>;
|
|
26
|
+
bySeverity: Record<string, number>;
|
|
27
|
+
byZone: Record<string, number>;
|
|
28
|
+
agentsUsed: string[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export declare class ParallelOrchestrator extends EventEmitter {
|
|
32
|
+
private manager;
|
|
33
|
+
private agents;
|
|
34
|
+
private initialized;
|
|
35
|
+
constructor(manager?: ZoneManager);
|
|
36
|
+
/**
|
|
37
|
+
* Initialize the orchestrator with all agents
|
|
38
|
+
*/
|
|
39
|
+
initialize(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Run a full security scan using Aura Protocol
|
|
42
|
+
*
|
|
43
|
+
* Execution flow:
|
|
44
|
+
* 1. Scanner Zone - Run all scanners in parallel
|
|
45
|
+
* 2. Policy Zone - Evaluate and validate findings (sequential)
|
|
46
|
+
*/
|
|
47
|
+
scan(config: OrchestratorConfig): Promise<OrchestratorResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Run only the scanner zone (faster, no policy evaluation)
|
|
50
|
+
*/
|
|
51
|
+
quickScan(targetPath: string): Promise<OrchestratorResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Run a full scan with policy evaluation
|
|
54
|
+
*/
|
|
55
|
+
fullScan(targetPath: string): Promise<OrchestratorResult>;
|
|
56
|
+
/**
|
|
57
|
+
* Get available agents
|
|
58
|
+
*/
|
|
59
|
+
getAvailableAgents(): Promise<Agent[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Get current state for visualization
|
|
62
|
+
*/
|
|
63
|
+
getState(): ReturnType<ZoneManager['exportState']>;
|
|
64
|
+
/**
|
|
65
|
+
* Reset all zones
|
|
66
|
+
*/
|
|
67
|
+
reset(): void;
|
|
68
|
+
private buildSummary;
|
|
69
|
+
}
|
|
70
|
+
export declare const orchestrator: ParallelOrchestrator;
|
|
71
|
+
export * from '../zones/types.js';
|
|
72
|
+
export * from '../zones/manager.js';
|