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,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aura Protocol - Trivy Agent
|
|
3
|
+
*
|
|
4
|
+
* Scans for vulnerabilities in dependencies and containers.
|
|
5
|
+
*/
|
|
6
|
+
import { BaseAgent } from '../base.js';
|
|
7
|
+
const CONFIG = {
|
|
8
|
+
id: 'trivy',
|
|
9
|
+
name: 'Trivy',
|
|
10
|
+
role: 'scanner',
|
|
11
|
+
description: 'Scan for vulnerabilities in dependencies and containers',
|
|
12
|
+
enabled: true,
|
|
13
|
+
externalTool: 'trivy',
|
|
14
|
+
};
|
|
15
|
+
const CAPABILITIES = {
|
|
16
|
+
fileTypes: ['package.json', 'package-lock.json', 'requirements.txt', 'Gemfile', 'go.mod', 'Cargo.toml'],
|
|
17
|
+
languages: ['javascript', 'typescript', 'python', 'ruby', 'go', 'rust'],
|
|
18
|
+
requiresExternalTool: true,
|
|
19
|
+
supportsParallel: true,
|
|
20
|
+
};
|
|
21
|
+
export class TrivyAgent extends BaseAgent {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(CONFIG, CAPABILITIES);
|
|
24
|
+
}
|
|
25
|
+
async execute(context) {
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
this.status = 'running';
|
|
28
|
+
const findings = [];
|
|
29
|
+
try {
|
|
30
|
+
context.log('info', `Trivy scanning: ${context.targetPath}`);
|
|
31
|
+
// Run trivy filesystem scan
|
|
32
|
+
const { stdout, stderr, exitCode } = await this.executeCommand('trivy', [
|
|
33
|
+
'fs',
|
|
34
|
+
'--format',
|
|
35
|
+
'json',
|
|
36
|
+
'--scanners',
|
|
37
|
+
'vuln',
|
|
38
|
+
'--skip-dirs',
|
|
39
|
+
'node_modules,.git,dist,build',
|
|
40
|
+
context.targetPath,
|
|
41
|
+
], { cwd: context.targetPath, timeout: 300000 });
|
|
42
|
+
if (stdout.trim()) {
|
|
43
|
+
try {
|
|
44
|
+
const output = JSON.parse(stdout);
|
|
45
|
+
if (output.Results) {
|
|
46
|
+
for (const result of output.Results) {
|
|
47
|
+
if (result.Vulnerabilities) {
|
|
48
|
+
for (const vuln of result.Vulnerabilities) {
|
|
49
|
+
const finding = this.createFinding('trivy', {
|
|
50
|
+
type: 'vulnerability',
|
|
51
|
+
severity: this.mapSeverity(vuln.Severity),
|
|
52
|
+
title: `${vuln.PkgName}: ${vuln.VulnerabilityID}`,
|
|
53
|
+
description: vuln.Title || vuln.Description || `Vulnerability in ${vuln.PkgName}`,
|
|
54
|
+
file: result.Target,
|
|
55
|
+
metadata: {
|
|
56
|
+
vulnerabilityId: vuln.VulnerabilityID,
|
|
57
|
+
package: vuln.PkgName,
|
|
58
|
+
installedVersion: vuln.InstalledVersion,
|
|
59
|
+
fixedVersion: vuln.FixedVersion,
|
|
60
|
+
references: vuln.References?.slice(0, 3),
|
|
61
|
+
cvssScore: this.getCvssScore(vuln),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
findings.push(finding);
|
|
65
|
+
context.addFinding(finding);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (parseError) {
|
|
72
|
+
context.log('warn', `Failed to parse trivy output: ${parseError}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
this.status = 'complete';
|
|
76
|
+
context.log('info', `Trivy found ${findings.length} vulnerabilities`);
|
|
77
|
+
return {
|
|
78
|
+
agentId: this.config.id,
|
|
79
|
+
agentName: this.config.name,
|
|
80
|
+
status: 'success',
|
|
81
|
+
findings,
|
|
82
|
+
duration: Date.now() - startTime,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
this.status = 'error';
|
|
87
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
88
|
+
context.log('error', `Trivy error: ${errorMsg}`);
|
|
89
|
+
return {
|
|
90
|
+
agentId: this.config.id,
|
|
91
|
+
agentName: this.config.name,
|
|
92
|
+
status: 'error',
|
|
93
|
+
findings,
|
|
94
|
+
duration: Date.now() - startTime,
|
|
95
|
+
error: errorMsg,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
mapSeverity(trivySeverity) {
|
|
100
|
+
switch (trivySeverity.toUpperCase()) {
|
|
101
|
+
case 'CRITICAL':
|
|
102
|
+
return 'critical';
|
|
103
|
+
case 'HIGH':
|
|
104
|
+
return 'high';
|
|
105
|
+
case 'MEDIUM':
|
|
106
|
+
return 'medium';
|
|
107
|
+
case 'LOW':
|
|
108
|
+
return 'low';
|
|
109
|
+
default:
|
|
110
|
+
return 'info';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
getCvssScore(vuln) {
|
|
114
|
+
if (!vuln.CVSS)
|
|
115
|
+
return undefined;
|
|
116
|
+
for (const source of Object.values(vuln.CVSS)) {
|
|
117
|
+
if (source.V3Score)
|
|
118
|
+
return source.V3Score;
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aura Protocol - Agent Types
|
|
3
|
+
*
|
|
4
|
+
* Agents are specialized workers that execute within zones.
|
|
5
|
+
* Each agent has a specific role and can only operate within its assigned zone.
|
|
6
|
+
*/
|
|
7
|
+
import { ZoneContext, ZoneFinding, AgentResult } from '../zones/types.js';
|
|
8
|
+
export { AgentResult } from '../zones/types.js';
|
|
9
|
+
export type AgentRole = 'scanner' | 'policy' | 'validator' | 'reporter' | 'notifier';
|
|
10
|
+
export type AgentStatus = 'idle' | 'running' | 'complete' | 'error' | 'disabled';
|
|
11
|
+
export interface AgentConfig {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
role: AgentRole;
|
|
15
|
+
description: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
externalTool?: string;
|
|
18
|
+
config?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface AgentCapabilities {
|
|
21
|
+
fileTypes?: string[];
|
|
22
|
+
languages?: string[];
|
|
23
|
+
requiresExternalTool: boolean;
|
|
24
|
+
supportsParallel: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Base Agent Interface
|
|
28
|
+
*
|
|
29
|
+
* All agents must implement this interface.
|
|
30
|
+
*/
|
|
31
|
+
export interface Agent {
|
|
32
|
+
readonly config: AgentConfig;
|
|
33
|
+
readonly capabilities: AgentCapabilities;
|
|
34
|
+
/**
|
|
35
|
+
* Check if the agent is available (external tools installed, etc.)
|
|
36
|
+
*/
|
|
37
|
+
isAvailable(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Execute the agent within a zone context
|
|
40
|
+
*/
|
|
41
|
+
execute(context: ZoneContext): Promise<AgentResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Get agent status
|
|
44
|
+
*/
|
|
45
|
+
getStatus(): AgentStatus;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Scanner Agent Interface
|
|
49
|
+
*
|
|
50
|
+
* Agents that scan for secrets, vulnerabilities, etc.
|
|
51
|
+
*/
|
|
52
|
+
export interface ScannerAgent extends Agent {
|
|
53
|
+
readonly config: AgentConfig & {
|
|
54
|
+
role: 'scanner';
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Run the scan and return findings
|
|
58
|
+
*/
|
|
59
|
+
scan(targetPath: string, context: ZoneContext): Promise<ZoneFinding[]>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Policy Agent Interface
|
|
63
|
+
*
|
|
64
|
+
* Agents that evaluate policies and context
|
|
65
|
+
*/
|
|
66
|
+
export interface PolicyAgent extends Agent {
|
|
67
|
+
readonly config: AgentConfig & {
|
|
68
|
+
role: 'policy';
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Evaluate findings against policies
|
|
72
|
+
*/
|
|
73
|
+
evaluate(findings: ZoneFinding[], context: ZoneContext): Promise<{
|
|
74
|
+
filtered: ZoneFinding[];
|
|
75
|
+
falsePositives: ZoneFinding[];
|
|
76
|
+
escalated: ZoneFinding[];
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validator Agent Interface
|
|
81
|
+
*
|
|
82
|
+
* Agents that validate and deduplicate findings
|
|
83
|
+
*/
|
|
84
|
+
export interface IValidatorAgent extends Agent {
|
|
85
|
+
readonly config: AgentConfig & {
|
|
86
|
+
role: 'validator';
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Validate findings and remove false positives
|
|
90
|
+
*/
|
|
91
|
+
validate(findings: ZoneFinding[], context: ZoneContext): Promise<ZoneFinding[]>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Reporter Agent Interface
|
|
95
|
+
*
|
|
96
|
+
* Agents that generate reports
|
|
97
|
+
*/
|
|
98
|
+
export interface ReporterAgent extends Agent {
|
|
99
|
+
readonly config: AgentConfig & {
|
|
100
|
+
role: 'reporter';
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Generate a report from findings
|
|
104
|
+
*/
|
|
105
|
+
generateReport(findings: ZoneFinding[], format: 'sarif' | 'json' | 'html' | 'markdown', context: ZoneContext): Promise<string>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Notifier Agent Interface
|
|
109
|
+
*
|
|
110
|
+
* Agents that send notifications
|
|
111
|
+
*/
|
|
112
|
+
export interface NotifierAgent extends Agent {
|
|
113
|
+
readonly config: AgentConfig & {
|
|
114
|
+
role: 'notifier';
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Send notification about findings
|
|
118
|
+
*/
|
|
119
|
+
notify(findings: ZoneFinding[], channel: string, context: ZoneContext): Promise<boolean>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Agent Registry
|
|
123
|
+
*
|
|
124
|
+
* Stores all available agents
|
|
125
|
+
*/
|
|
126
|
+
export interface AgentRegistry {
|
|
127
|
+
register(agent: Agent): void;
|
|
128
|
+
unregister(agentId: string): void;
|
|
129
|
+
get(agentId: string): Agent | undefined;
|
|
130
|
+
getAll(): Agent[];
|
|
131
|
+
getByRole(role: AgentRole): Agent[];
|
|
132
|
+
getAvailable(): Promise<Agent[]>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Default agent configurations
|
|
136
|
+
*/
|
|
137
|
+
export declare const DEFAULT_AGENTS: AgentConfig[];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aura Protocol - Agent Types
|
|
3
|
+
*
|
|
4
|
+
* Agents are specialized workers that execute within zones.
|
|
5
|
+
* Each agent has a specific role and can only operate within its assigned zone.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Default agent configurations
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_AGENTS = [
|
|
11
|
+
// Scanner agents
|
|
12
|
+
{
|
|
13
|
+
id: 'gitleaks',
|
|
14
|
+
name: 'Gitleaks',
|
|
15
|
+
role: 'scanner',
|
|
16
|
+
description: 'Detect secrets and API keys in code',
|
|
17
|
+
enabled: true,
|
|
18
|
+
externalTool: 'gitleaks',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'trivy',
|
|
22
|
+
name: 'Trivy',
|
|
23
|
+
role: 'scanner',
|
|
24
|
+
description: 'Scan for vulnerabilities in dependencies and containers',
|
|
25
|
+
enabled: true,
|
|
26
|
+
externalTool: 'trivy',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'semgrep',
|
|
30
|
+
name: 'Semgrep',
|
|
31
|
+
role: 'scanner',
|
|
32
|
+
description: 'Static analysis for security patterns',
|
|
33
|
+
enabled: true,
|
|
34
|
+
externalTool: 'semgrep',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'grype',
|
|
38
|
+
name: 'Grype',
|
|
39
|
+
role: 'scanner',
|
|
40
|
+
description: 'Vulnerability scanner for container images and filesystems',
|
|
41
|
+
enabled: true,
|
|
42
|
+
externalTool: 'grype',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'npm-audit',
|
|
46
|
+
name: 'NPM Audit',
|
|
47
|
+
role: 'scanner',
|
|
48
|
+
description: 'Audit npm packages for vulnerabilities',
|
|
49
|
+
enabled: true,
|
|
50
|
+
externalTool: 'npm',
|
|
51
|
+
},
|
|
52
|
+
// Policy agents
|
|
53
|
+
{
|
|
54
|
+
id: 'policy-evaluator',
|
|
55
|
+
name: 'Policy Evaluator',
|
|
56
|
+
role: 'policy',
|
|
57
|
+
description: 'Evaluate findings against security policies',
|
|
58
|
+
enabled: true,
|
|
59
|
+
},
|
|
60
|
+
// Validator agents
|
|
61
|
+
{
|
|
62
|
+
id: 'validator',
|
|
63
|
+
name: 'Finding Validator',
|
|
64
|
+
role: 'validator',
|
|
65
|
+
description: 'Validate and deduplicate findings, remove false positives',
|
|
66
|
+
enabled: true,
|
|
67
|
+
},
|
|
68
|
+
// Reporter agents
|
|
69
|
+
{
|
|
70
|
+
id: 'sarif-reporter',
|
|
71
|
+
name: 'SARIF Reporter',
|
|
72
|
+
role: 'reporter',
|
|
73
|
+
description: 'Generate SARIF format reports',
|
|
74
|
+
enabled: true,
|
|
75
|
+
},
|
|
76
|
+
// Notifier agents
|
|
77
|
+
{
|
|
78
|
+
id: 'slack-notifier',
|
|
79
|
+
name: 'Slack Notifier',
|
|
80
|
+
role: 'notifier',
|
|
81
|
+
description: 'Send notifications to Slack',
|
|
82
|
+
enabled: true,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'discord-notifier',
|
|
86
|
+
name: 'Discord Notifier',
|
|
87
|
+
role: 'notifier',
|
|
88
|
+
description: 'Send notifications to Discord',
|
|
89
|
+
enabled: true,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AuditorOutput } from '../types/events.js';
|
|
2
|
+
import { AuraClient } from '../aura/client.js';
|
|
3
|
+
export interface PipelineConfig {
|
|
4
|
+
auraClient: AuraClient;
|
|
5
|
+
strictMode?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare class AuditorPipeline {
|
|
8
|
+
private validator;
|
|
9
|
+
private client;
|
|
10
|
+
private strictMode;
|
|
11
|
+
constructor(config: PipelineConfig);
|
|
12
|
+
analyze(rawInput: unknown): Promise<AuditorOutput>;
|
|
13
|
+
private runAnalysisRules;
|
|
14
|
+
private findCriticalAssetChanges;
|
|
15
|
+
private detectSecretPatterns;
|
|
16
|
+
private parseVulnScan;
|
|
17
|
+
private getSeverityForRisk;
|
|
18
|
+
private createEvent;
|
|
19
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Auditor Pipeline - Rule-based security analysis with fail-closed behavior
|
|
2
|
+
import { SchemaValidator } from './validator.js';
|
|
3
|
+
import { AuraConnectionError } from '../aura/client.js';
|
|
4
|
+
const AGENT_ID = 'exploit-reviewer';
|
|
5
|
+
export class AuditorPipeline {
|
|
6
|
+
validator;
|
|
7
|
+
client;
|
|
8
|
+
strictMode;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.validator = new SchemaValidator();
|
|
11
|
+
this.client = config.auraClient;
|
|
12
|
+
this.strictMode = config.strictMode ?? true;
|
|
13
|
+
}
|
|
14
|
+
// Main entry point - fail-closed on any error
|
|
15
|
+
async analyze(rawInput) {
|
|
16
|
+
// Fail-closed: must be connected to Aura
|
|
17
|
+
if (!this.client.connected) {
|
|
18
|
+
throw new AuraConnectionError('No Aura connection - blocking execution');
|
|
19
|
+
}
|
|
20
|
+
// Fail-closed: validate input
|
|
21
|
+
this.validator.assertValidInput(rawInput);
|
|
22
|
+
const input = rawInput;
|
|
23
|
+
// Start analysis
|
|
24
|
+
const events = [];
|
|
25
|
+
const assumptions = [];
|
|
26
|
+
const uncertainties = [];
|
|
27
|
+
// Emit analysis_started
|
|
28
|
+
events.push(this.createEvent('analysis_started', 'self', {
|
|
29
|
+
severity: 'low',
|
|
30
|
+
claim: `Analyzing ${input.change_event.type} in ${input.change_event.environment}`,
|
|
31
|
+
attack_path: ['Initiated audit pipeline'],
|
|
32
|
+
affected_assets: [],
|
|
33
|
+
evidence_refs: [{ type: 'diff', pointer: input.change_event.id }],
|
|
34
|
+
assurance_break: [],
|
|
35
|
+
confidence: 1.0
|
|
36
|
+
}));
|
|
37
|
+
// Run analysis rules
|
|
38
|
+
const findings = this.runAnalysisRules(input, assumptions, uncertainties);
|
|
39
|
+
events.push(...findings);
|
|
40
|
+
// Determine final state
|
|
41
|
+
const hasCritical = findings.some(e => e.payload.severity === 'critical');
|
|
42
|
+
const hasHigh = findings.some(e => e.payload.severity === 'high');
|
|
43
|
+
const hasConflict = findings.some(e => e.event_type === 'conflict_detected');
|
|
44
|
+
let agentState = 'idle';
|
|
45
|
+
if (hasCritical) {
|
|
46
|
+
agentState = 'blocked';
|
|
47
|
+
}
|
|
48
|
+
else if (hasConflict) {
|
|
49
|
+
agentState = 'conflict';
|
|
50
|
+
}
|
|
51
|
+
else if (hasHigh) {
|
|
52
|
+
agentState = 'escalated';
|
|
53
|
+
}
|
|
54
|
+
else if (findings.length > 0) {
|
|
55
|
+
agentState = 'analyzing';
|
|
56
|
+
}
|
|
57
|
+
const output = {
|
|
58
|
+
agent_id: AGENT_ID,
|
|
59
|
+
agent_state: agentState,
|
|
60
|
+
events,
|
|
61
|
+
meta: { assumptions, uncertainties }
|
|
62
|
+
};
|
|
63
|
+
// Fail-closed: validate output
|
|
64
|
+
this.validator.assertValidOutput(output);
|
|
65
|
+
// Publish to Aura memory (audit log)
|
|
66
|
+
await this.client.publishToMemory({
|
|
67
|
+
key: `audit:${input.change_event.id}:${Date.now()}`,
|
|
68
|
+
value: output,
|
|
69
|
+
metadata: {
|
|
70
|
+
commit: input.change_event.commit,
|
|
71
|
+
repo: input.change_event.repo,
|
|
72
|
+
environment: input.change_event.environment
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return output;
|
|
76
|
+
}
|
|
77
|
+
runAnalysisRules(input, assumptions, uncertainties) {
|
|
78
|
+
const findings = [];
|
|
79
|
+
const { change_event, evidence_bundle, policy_context } = input;
|
|
80
|
+
// Rule: Critical asset modification
|
|
81
|
+
const criticalFiles = this.findCriticalAssetChanges(change_event.files_changed, policy_context.critical_assets);
|
|
82
|
+
if (criticalFiles.length > 0) {
|
|
83
|
+
findings.push(this.createEvent('finding_raised', 'self', {
|
|
84
|
+
severity: this.getSeverityForRisk(policy_context.risk_tolerance, 'high'),
|
|
85
|
+
claim: 'Critical asset files modified without explicit approval chain',
|
|
86
|
+
attack_path: [
|
|
87
|
+
'Attacker gains commit access',
|
|
88
|
+
'Modifies critical asset configuration',
|
|
89
|
+
'Changes bypass standard review due to file location'
|
|
90
|
+
],
|
|
91
|
+
affected_assets: criticalFiles,
|
|
92
|
+
evidence_refs: criticalFiles.map(f => ({ type: 'diff', pointer: f })),
|
|
93
|
+
assurance_break: ['integrity', 'access_control'],
|
|
94
|
+
confidence: 0.85
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
// Rule: Production deployment without staging
|
|
98
|
+
if (change_event.environment === 'prod' && change_event.type === 'deploy') {
|
|
99
|
+
assumptions.push('Assumes staging validation is required before prod');
|
|
100
|
+
findings.push(this.createEvent('finding_raised', 'self', {
|
|
101
|
+
severity: this.getSeverityForRisk(policy_context.risk_tolerance, 'medium'),
|
|
102
|
+
claim: 'Direct production deployment detected',
|
|
103
|
+
attack_path: [
|
|
104
|
+
'Change bypasses staging environment',
|
|
105
|
+
'Untested code reaches production',
|
|
106
|
+
'Runtime errors expose attack surface'
|
|
107
|
+
],
|
|
108
|
+
affected_assets: ['production-environment'],
|
|
109
|
+
evidence_refs: [{ type: 'diff', pointer: change_event.commit }],
|
|
110
|
+
assurance_break: ['isolation'],
|
|
111
|
+
confidence: 0.7
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
// Rule: Check for secrets in diff
|
|
115
|
+
const secretPatterns = this.detectSecretPatterns(change_event.diff);
|
|
116
|
+
if (secretPatterns.length > 0) {
|
|
117
|
+
findings.push(this.createEvent('escalation_triggered', 'self', {
|
|
118
|
+
severity: 'critical',
|
|
119
|
+
claim: 'Potential secrets or credentials detected in diff',
|
|
120
|
+
attack_path: [
|
|
121
|
+
'Credentials committed to repository',
|
|
122
|
+
'Secrets exposed in version history',
|
|
123
|
+
'Attacker extracts credentials from git history'
|
|
124
|
+
],
|
|
125
|
+
affected_assets: secretPatterns.map(p => p.file),
|
|
126
|
+
evidence_refs: secretPatterns.map(p => ({ type: 'diff', pointer: p.line })),
|
|
127
|
+
assurance_break: ['integrity', 'access_control'],
|
|
128
|
+
confidence: 0.95
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
// Rule: Vulnerability scan findings
|
|
132
|
+
if (evidence_bundle.vuln_scan) {
|
|
133
|
+
const vulnFindings = this.parseVulnScan(evidence_bundle.vuln_scan);
|
|
134
|
+
if (vulnFindings.critical > 0 || vulnFindings.high > 0) {
|
|
135
|
+
findings.push(this.createEvent('finding_raised', 'self', {
|
|
136
|
+
severity: vulnFindings.critical > 0 ? 'critical' : 'high',
|
|
137
|
+
claim: `Vulnerability scan detected ${vulnFindings.critical} critical and ${vulnFindings.high} high severity issues`,
|
|
138
|
+
attack_path: [
|
|
139
|
+
'Known vulnerability present in dependencies',
|
|
140
|
+
'Attacker identifies CVE in deployed version',
|
|
141
|
+
'Exploit executed against vulnerable component'
|
|
142
|
+
],
|
|
143
|
+
affected_assets: ['dependencies'],
|
|
144
|
+
evidence_refs: [{ type: 'scan', pointer: 'vuln_scan' }],
|
|
145
|
+
assurance_break: ['integrity'],
|
|
146
|
+
confidence: 0.9
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Rule: Infrastructure changes
|
|
151
|
+
if (change_event.type === 'infra_change') {
|
|
152
|
+
uncertainties.push('Infrastructure change impact depends on cloud provider specifics');
|
|
153
|
+
findings.push(this.createEvent('finding_raised', 'self', {
|
|
154
|
+
severity: this.getSeverityForRisk(policy_context.risk_tolerance, 'medium'),
|
|
155
|
+
claim: 'Infrastructure modification requires manual review',
|
|
156
|
+
attack_path: [
|
|
157
|
+
'IaC change modifies security boundaries',
|
|
158
|
+
'Misconfiguration exposes internal services',
|
|
159
|
+
'Attacker gains network access to protected resources'
|
|
160
|
+
],
|
|
161
|
+
affected_assets: ['infrastructure'],
|
|
162
|
+
evidence_refs: change_event.files_changed.map(f => ({ type: 'diff', pointer: f })),
|
|
163
|
+
assurance_break: ['isolation', 'access_control'],
|
|
164
|
+
confidence: 0.6
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
return findings;
|
|
168
|
+
}
|
|
169
|
+
findCriticalAssetChanges(files, criticalAssets) {
|
|
170
|
+
const matches = [];
|
|
171
|
+
for (const file of files) {
|
|
172
|
+
const fileLower = file.toLowerCase();
|
|
173
|
+
for (const asset of criticalAssets) {
|
|
174
|
+
if (fileLower.includes(asset.toLowerCase())) {
|
|
175
|
+
matches.push(file);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return matches;
|
|
181
|
+
}
|
|
182
|
+
detectSecretPatterns(diff) {
|
|
183
|
+
const patterns = [
|
|
184
|
+
/api[_-]?key\s*[=:]\s*['"][^'"]+['"]/gi,
|
|
185
|
+
/secret\s*[=:]\s*['"][^'"]+['"]/gi,
|
|
186
|
+
/password\s*[=:]\s*['"][^'"]+['"]/gi,
|
|
187
|
+
/bearer\s+[a-z0-9_-]+/gi,
|
|
188
|
+
/-----BEGIN\s+(RSA|EC|OPENSSH)\s+PRIVATE\s+KEY-----/g,
|
|
189
|
+
/aws[_-]?secret[_-]?access[_-]?key/gi
|
|
190
|
+
];
|
|
191
|
+
const findings = [];
|
|
192
|
+
const lines = diff.split('\n');
|
|
193
|
+
for (let i = 0; i < lines.length; i++) {
|
|
194
|
+
const line = lines[i];
|
|
195
|
+
if (line.startsWith('+')) {
|
|
196
|
+
for (const pattern of patterns) {
|
|
197
|
+
if (pattern.test(line)) {
|
|
198
|
+
findings.push({ file: 'diff', line: `line:${i + 1}` });
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return findings;
|
|
205
|
+
}
|
|
206
|
+
parseVulnScan(scan) {
|
|
207
|
+
// Simple parser - in production would parse actual scanner output
|
|
208
|
+
const criticalMatch = scan.match(/critical[:\s]+(\d+)/i);
|
|
209
|
+
const highMatch = scan.match(/high[:\s]+(\d+)/i);
|
|
210
|
+
return {
|
|
211
|
+
critical: criticalMatch ? parseInt(criticalMatch[1], 10) : 0,
|
|
212
|
+
high: highMatch ? parseInt(highMatch[1], 10) : 0
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
getSeverityForRisk(tolerance, baseSeverity) {
|
|
216
|
+
if (tolerance === 'low') {
|
|
217
|
+
if (baseSeverity === 'low')
|
|
218
|
+
return 'medium';
|
|
219
|
+
if (baseSeverity === 'medium')
|
|
220
|
+
return 'high';
|
|
221
|
+
return 'critical';
|
|
222
|
+
}
|
|
223
|
+
if (tolerance === 'high') {
|
|
224
|
+
if (baseSeverity === 'high')
|
|
225
|
+
return 'medium';
|
|
226
|
+
if (baseSeverity === 'medium')
|
|
227
|
+
return 'low';
|
|
228
|
+
return 'low';
|
|
229
|
+
}
|
|
230
|
+
return baseSeverity;
|
|
231
|
+
}
|
|
232
|
+
createEvent(eventType, target, payload) {
|
|
233
|
+
return {
|
|
234
|
+
event_type: eventType,
|
|
235
|
+
target,
|
|
236
|
+
payload,
|
|
237
|
+
timestamp: new Date().toISOString()
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AuditorInput, AuditorOutput } from '../types/events.js';
|
|
2
|
+
export declare class ValidationError extends Error {
|
|
3
|
+
readonly errors: unknown[];
|
|
4
|
+
constructor(message: string, errors: unknown[]);
|
|
5
|
+
}
|
|
6
|
+
export declare class SchemaValidator {
|
|
7
|
+
private ajv;
|
|
8
|
+
private validateInput;
|
|
9
|
+
private validateOutput;
|
|
10
|
+
constructor();
|
|
11
|
+
assertValidInput(data: unknown): asserts data is AuditorInput;
|
|
12
|
+
assertValidOutput(data: unknown): asserts data is AuditorOutput;
|
|
13
|
+
isValidInput(data: unknown): data is AuditorInput;
|
|
14
|
+
isValidOutput(data: unknown): data is AuditorOutput;
|
|
15
|
+
getInputErrors(data: unknown): unknown[];
|
|
16
|
+
getOutputErrors(data: unknown): unknown[];
|
|
17
|
+
}
|