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,41 @@
|
|
|
1
|
+
import type { AuditorInput, AuditorOutput, ChangeEvent, EvidenceBundle, PolicyContext } from '../types/events.js';
|
|
2
|
+
export interface AuditClientConfig {
|
|
3
|
+
serverUrl?: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
retries?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface AuditRequest {
|
|
9
|
+
changeEvent: ChangeEvent;
|
|
10
|
+
evidenceBundle?: Partial<EvidenceBundle>;
|
|
11
|
+
policyContext?: Partial<PolicyContext>;
|
|
12
|
+
}
|
|
13
|
+
export interface AuditResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
output?: AuditorOutput;
|
|
16
|
+
error?: string;
|
|
17
|
+
duration: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ServerInfo {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
endpoints: string[];
|
|
23
|
+
tools: string[];
|
|
24
|
+
}
|
|
25
|
+
export declare class AuditClient {
|
|
26
|
+
private config;
|
|
27
|
+
constructor(config?: AuditClientConfig);
|
|
28
|
+
private headers;
|
|
29
|
+
private fetchWithRetry;
|
|
30
|
+
private delay;
|
|
31
|
+
getServerInfo(): Promise<ServerInfo>;
|
|
32
|
+
isHealthy(): Promise<boolean>;
|
|
33
|
+
audit(request: AuditRequest): Promise<AuditResult>;
|
|
34
|
+
getAuditLogs(): Promise<string[]>;
|
|
35
|
+
getAuditEntry(key: string): Promise<AuditorOutput | null>;
|
|
36
|
+
watchAudits(pollInterval?: number): AsyncGenerator<AuditorOutput>;
|
|
37
|
+
}
|
|
38
|
+
export declare function createPullRequestEvent(repo: string, commit: string, filesChanged: string[], diff: string, environment?: 'dev' | 'staging' | 'prod'): ChangeEvent;
|
|
39
|
+
export declare function createDeployEvent(repo: string, commit: string, environment: 'dev' | 'staging' | 'prod'): ChangeEvent;
|
|
40
|
+
export declare function createInfraChangeEvent(repo: string, commit: string, filesChanged: string[], diff: string): ChangeEvent;
|
|
41
|
+
export { AuditorInput, AuditorOutput, ChangeEvent, EvidenceBundle, PolicyContext };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Aura Client SDK - High-level API for interacting with the auditor
|
|
2
|
+
// Provides typed methods, automatic retries, and event streaming
|
|
3
|
+
export class AuditClient {
|
|
4
|
+
config;
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.config = {
|
|
7
|
+
serverUrl: config.serverUrl ?? 'http://127.0.0.1:3000',
|
|
8
|
+
apiKey: config.apiKey ?? '',
|
|
9
|
+
timeout: config.timeout ?? 30000,
|
|
10
|
+
retries: config.retries ?? 3
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
headers() {
|
|
14
|
+
const h = {
|
|
15
|
+
'Content-Type': 'application/json'
|
|
16
|
+
};
|
|
17
|
+
if (this.config.apiKey) {
|
|
18
|
+
h['Authorization'] = `Bearer ${this.config.apiKey}`;
|
|
19
|
+
}
|
|
20
|
+
return h;
|
|
21
|
+
}
|
|
22
|
+
async fetchWithRetry(url, options, retries = this.config.retries) {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(url, {
|
|
27
|
+
...options,
|
|
28
|
+
signal: controller.signal
|
|
29
|
+
});
|
|
30
|
+
clearTimeout(timeoutId);
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
return await res.json();
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
clearTimeout(timeoutId);
|
|
38
|
+
if (retries > 0 && !(err instanceof DOMException && err.name === 'AbortError')) {
|
|
39
|
+
await this.delay(1000);
|
|
40
|
+
return this.fetchWithRetry(url, options, retries - 1);
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
delay(ms) {
|
|
46
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
47
|
+
}
|
|
48
|
+
async getServerInfo() {
|
|
49
|
+
return this.fetchWithRetry(`${this.config.serverUrl}/info`, { method: 'GET', headers: this.headers() });
|
|
50
|
+
}
|
|
51
|
+
async isHealthy() {
|
|
52
|
+
try {
|
|
53
|
+
await this.getServerInfo();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async audit(request) {
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
const input = {
|
|
63
|
+
change_event: request.changeEvent,
|
|
64
|
+
evidence_bundle: {
|
|
65
|
+
sbom: request.evidenceBundle?.sbom,
|
|
66
|
+
vuln_scan: request.evidenceBundle?.vuln_scan,
|
|
67
|
+
sast_results: request.evidenceBundle?.sast_results,
|
|
68
|
+
iac_scan: request.evidenceBundle?.iac_scan,
|
|
69
|
+
provenance: request.evidenceBundle?.provenance,
|
|
70
|
+
runtime_delta: request.evidenceBundle?.runtime_delta
|
|
71
|
+
},
|
|
72
|
+
policy_context: {
|
|
73
|
+
critical_assets: request.policyContext?.critical_assets ?? ['auth', 'billing', 'phi', 'infra'],
|
|
74
|
+
risk_tolerance: request.policyContext?.risk_tolerance ?? 'medium'
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
const response = await this.fetchWithRetry(`${this.config.serverUrl}/tools`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: this.headers(),
|
|
81
|
+
body: JSON.stringify({ tool: 'audit', arguments: input })
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
output: response.result,
|
|
86
|
+
duration: Date.now() - startTime
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: err instanceof Error ? err.message : String(err),
|
|
93
|
+
duration: Date.now() - startTime
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async getAuditLogs() {
|
|
98
|
+
const response = await this.fetchWithRetry(`${this.config.serverUrl}/memory`, { method: 'GET', headers: this.headers() });
|
|
99
|
+
return response.keys;
|
|
100
|
+
}
|
|
101
|
+
async getAuditEntry(key) {
|
|
102
|
+
try {
|
|
103
|
+
const response = await this.fetchWithRetry(`${this.config.serverUrl}/memory?key=${encodeURIComponent(key)}`, { method: 'GET', headers: this.headers() });
|
|
104
|
+
return response.value;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Stream audit logs in real-time
|
|
111
|
+
async *watchAudits(pollInterval = 2000) {
|
|
112
|
+
let lastCount = 0;
|
|
113
|
+
const seenKeys = new Set();
|
|
114
|
+
while (true) {
|
|
115
|
+
try {
|
|
116
|
+
const keys = await this.getAuditLogs();
|
|
117
|
+
if (keys.length > lastCount) {
|
|
118
|
+
for (const key of keys) {
|
|
119
|
+
if (!seenKeys.has(key)) {
|
|
120
|
+
seenKeys.add(key);
|
|
121
|
+
const entry = await this.getAuditEntry(key);
|
|
122
|
+
if (entry) {
|
|
123
|
+
yield entry;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
lastCount = keys.length;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Ignore errors in watch mode
|
|
132
|
+
}
|
|
133
|
+
await this.delay(pollInterval);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Helper functions for building audit requests
|
|
138
|
+
export function createPullRequestEvent(repo, commit, filesChanged, diff, environment = 'dev') {
|
|
139
|
+
return {
|
|
140
|
+
id: `pr-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
141
|
+
type: 'pull_request',
|
|
142
|
+
environment,
|
|
143
|
+
repo,
|
|
144
|
+
commit,
|
|
145
|
+
files_changed: filesChanged,
|
|
146
|
+
diff
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export function createDeployEvent(repo, commit, environment) {
|
|
150
|
+
return {
|
|
151
|
+
id: `deploy-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
152
|
+
type: 'deploy',
|
|
153
|
+
environment,
|
|
154
|
+
repo,
|
|
155
|
+
commit,
|
|
156
|
+
files_changed: [],
|
|
157
|
+
diff: ''
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export function createInfraChangeEvent(repo, commit, filesChanged, diff) {
|
|
161
|
+
return {
|
|
162
|
+
id: `infra-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
163
|
+
type: 'infra_change',
|
|
164
|
+
environment: 'prod',
|
|
165
|
+
repo,
|
|
166
|
+
commit,
|
|
167
|
+
files_changed: filesChanged,
|
|
168
|
+
diff
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface LicenseInfo {
|
|
2
|
+
package: string;
|
|
3
|
+
version: string;
|
|
4
|
+
license: string;
|
|
5
|
+
licenseFile?: string;
|
|
6
|
+
repository?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LicensePolicy {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
allowed: string[];
|
|
12
|
+
denied: string[];
|
|
13
|
+
unknown: 'allow' | 'warn' | 'deny';
|
|
14
|
+
}
|
|
15
|
+
export interface LicenseViolation {
|
|
16
|
+
package: string;
|
|
17
|
+
version: string;
|
|
18
|
+
license: string;
|
|
19
|
+
violation: string;
|
|
20
|
+
severity: 'high' | 'medium' | 'low';
|
|
21
|
+
}
|
|
22
|
+
export interface LicenseCheckResult {
|
|
23
|
+
policy: string;
|
|
24
|
+
licenses: LicenseInfo[];
|
|
25
|
+
violations: LicenseViolation[];
|
|
26
|
+
summary: {
|
|
27
|
+
total: number;
|
|
28
|
+
compliant: number;
|
|
29
|
+
violations: number;
|
|
30
|
+
unknown: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare const POLICIES: Record<string, LicensePolicy>;
|
|
34
|
+
export interface LicenseCheckOptions {
|
|
35
|
+
policy?: string | LicensePolicy;
|
|
36
|
+
allowedLicenses?: string[];
|
|
37
|
+
deniedLicenses?: string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function checkLicenses(targetPath: string, options?: LicenseCheckOptions): LicenseCheckResult;
|
|
40
|
+
export declare const POLICY_NAMES: string[];
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// License Compliance Checker
|
|
2
|
+
// Checks dependencies for license compliance with configurable policies
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
import { existsSync, readFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
// ============ Built-in Policies ============
|
|
7
|
+
export const POLICIES = {
|
|
8
|
+
permissive: {
|
|
9
|
+
name: 'Permissive',
|
|
10
|
+
description: 'Only allow permissive open source licenses',
|
|
11
|
+
allowed: [
|
|
12
|
+
'MIT', 'ISC', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0',
|
|
13
|
+
'Unlicense', '0BSD', 'CC0-1.0', 'WTFPL', 'Zlib', 'BlueOak-1.0.0'
|
|
14
|
+
],
|
|
15
|
+
denied: [
|
|
16
|
+
'GPL-2.0', 'GPL-3.0', 'AGPL-3.0', 'LGPL-2.1', 'LGPL-3.0',
|
|
17
|
+
'GPL-2.0-only', 'GPL-3.0-only', 'AGPL-3.0-only',
|
|
18
|
+
'GPL-2.0-or-later', 'GPL-3.0-or-later', 'AGPL-3.0-or-later'
|
|
19
|
+
],
|
|
20
|
+
unknown: 'warn'
|
|
21
|
+
},
|
|
22
|
+
strict: {
|
|
23
|
+
name: 'Strict',
|
|
24
|
+
description: 'Strict policy - only well-known permissive licenses',
|
|
25
|
+
allowed: ['MIT', 'ISC', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0'],
|
|
26
|
+
denied: [
|
|
27
|
+
'GPL-2.0', 'GPL-3.0', 'AGPL-3.0', 'LGPL-2.1', 'LGPL-3.0',
|
|
28
|
+
'GPL-2.0-only', 'GPL-3.0-only', 'AGPL-3.0-only',
|
|
29
|
+
'SSPL-1.0', 'BSL-1.1', 'Elastic-2.0'
|
|
30
|
+
],
|
|
31
|
+
unknown: 'deny'
|
|
32
|
+
},
|
|
33
|
+
copyleft: {
|
|
34
|
+
name: 'Copyleft Allowed',
|
|
35
|
+
description: 'Allow copyleft licenses (for open source projects)',
|
|
36
|
+
allowed: [
|
|
37
|
+
'MIT', 'ISC', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0',
|
|
38
|
+
'GPL-2.0', 'GPL-3.0', 'LGPL-2.1', 'LGPL-3.0', 'MPL-2.0',
|
|
39
|
+
'GPL-2.0-only', 'GPL-3.0-only', 'LGPL-2.1-only', 'LGPL-3.0-only'
|
|
40
|
+
],
|
|
41
|
+
denied: ['AGPL-3.0', 'AGPL-3.0-only', 'SSPL-1.0'],
|
|
42
|
+
unknown: 'warn'
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
// ============ License Detection ============
|
|
46
|
+
function isToolAvailable(tool) {
|
|
47
|
+
try {
|
|
48
|
+
const result = spawnSync(tool, ['--version'], { encoding: 'utf-8', timeout: 5000 });
|
|
49
|
+
return result.status === 0;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Run license-checker for Node.js projects
|
|
56
|
+
function runLicenseChecker(targetPath) {
|
|
57
|
+
const licenses = [];
|
|
58
|
+
if (!existsSync(join(targetPath, 'package.json'))) {
|
|
59
|
+
return licenses;
|
|
60
|
+
}
|
|
61
|
+
// Try license-checker (npm package)
|
|
62
|
+
if (isToolAvailable('npx')) {
|
|
63
|
+
try {
|
|
64
|
+
const result = spawnSync('npx', ['license-checker', '--json', '--production'], {
|
|
65
|
+
cwd: targetPath,
|
|
66
|
+
encoding: 'utf-8',
|
|
67
|
+
timeout: 60000,
|
|
68
|
+
maxBuffer: 10 * 1024 * 1024
|
|
69
|
+
});
|
|
70
|
+
if (result.stdout) {
|
|
71
|
+
try {
|
|
72
|
+
const data = JSON.parse(result.stdout);
|
|
73
|
+
for (const [pkgKey, info] of Object.entries(data)) {
|
|
74
|
+
const match = pkgKey.match(/^(.+)@([^@]+)$/);
|
|
75
|
+
if (match) {
|
|
76
|
+
licenses.push({
|
|
77
|
+
package: match[1],
|
|
78
|
+
version: match[2],
|
|
79
|
+
license: info.licenses || 'UNKNOWN',
|
|
80
|
+
repository: info.repository,
|
|
81
|
+
licenseFile: info.licenseFile
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch { }
|
|
90
|
+
}
|
|
91
|
+
// Fallback: read from package-lock.json
|
|
92
|
+
if (licenses.length === 0) {
|
|
93
|
+
const lockPath = join(targetPath, 'package-lock.json');
|
|
94
|
+
if (existsSync(lockPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
|
|
97
|
+
const packages = lock.packages || {};
|
|
98
|
+
for (const [path, info] of Object.entries(packages)) {
|
|
99
|
+
if (path === '' || !path.includes('node_modules/'))
|
|
100
|
+
continue;
|
|
101
|
+
const pkgInfo = info;
|
|
102
|
+
const name = path.replace(/^node_modules\//, '').replace(/.*node_modules\//, '');
|
|
103
|
+
if (name && pkgInfo.version) {
|
|
104
|
+
licenses.push({
|
|
105
|
+
package: name,
|
|
106
|
+
version: pkgInfo.version,
|
|
107
|
+
license: pkgInfo.license || 'UNKNOWN'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch { }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return licenses;
|
|
116
|
+
}
|
|
117
|
+
// Run pip-licenses for Python projects
|
|
118
|
+
function runPipLicenses(targetPath) {
|
|
119
|
+
const licenses = [];
|
|
120
|
+
if (!existsSync(join(targetPath, 'requirements.txt')) &&
|
|
121
|
+
!existsSync(join(targetPath, 'pyproject.toml'))) {
|
|
122
|
+
return licenses;
|
|
123
|
+
}
|
|
124
|
+
if (isToolAvailable('pip-licenses')) {
|
|
125
|
+
try {
|
|
126
|
+
const result = spawnSync('pip-licenses', ['--format=json'], {
|
|
127
|
+
cwd: targetPath,
|
|
128
|
+
encoding: 'utf-8',
|
|
129
|
+
timeout: 60000
|
|
130
|
+
});
|
|
131
|
+
if (result.stdout) {
|
|
132
|
+
try {
|
|
133
|
+
const data = JSON.parse(result.stdout);
|
|
134
|
+
for (const pkg of data) {
|
|
135
|
+
licenses.push({
|
|
136
|
+
package: pkg.Name,
|
|
137
|
+
version: pkg.Version,
|
|
138
|
+
license: pkg.License || 'UNKNOWN'
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch { }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
}
|
|
147
|
+
return licenses;
|
|
148
|
+
}
|
|
149
|
+
// Normalize license identifier to SPDX format
|
|
150
|
+
function normalizeLicense(license) {
|
|
151
|
+
const normalized = license.trim().toUpperCase();
|
|
152
|
+
// Common mappings
|
|
153
|
+
const mappings = {
|
|
154
|
+
'MIT LICENSE': 'MIT',
|
|
155
|
+
'APACHE LICENSE 2.0': 'Apache-2.0',
|
|
156
|
+
'APACHE-2.0': 'Apache-2.0',
|
|
157
|
+
'APACHE 2.0': 'Apache-2.0',
|
|
158
|
+
'BSD': 'BSD-3-Clause',
|
|
159
|
+
'BSD LICENSE': 'BSD-3-Clause',
|
|
160
|
+
'BSD-2': 'BSD-2-Clause',
|
|
161
|
+
'BSD-3': 'BSD-3-Clause',
|
|
162
|
+
'ISC LICENSE': 'ISC',
|
|
163
|
+
'GPL': 'GPL-3.0',
|
|
164
|
+
'GPL V2': 'GPL-2.0',
|
|
165
|
+
'GPL V3': 'GPL-3.0',
|
|
166
|
+
'LGPL': 'LGPL-3.0',
|
|
167
|
+
'MPL': 'MPL-2.0',
|
|
168
|
+
'UNLICENSED': 'UNLICENSED',
|
|
169
|
+
'UNKNOWN': 'UNKNOWN',
|
|
170
|
+
'(MIT OR APACHE-2.0)': 'MIT', // Take first option for OR
|
|
171
|
+
'MIT AND CC-BY-3.0': 'MIT' // Take first option for AND
|
|
172
|
+
};
|
|
173
|
+
return mappings[normalized] || license;
|
|
174
|
+
}
|
|
175
|
+
// Check if a license matches the policy
|
|
176
|
+
function checkLicense(license, policy) {
|
|
177
|
+
const normalized = normalizeLicense(license);
|
|
178
|
+
// Check denied list first
|
|
179
|
+
for (const denied of policy.denied) {
|
|
180
|
+
if (normalized.includes(denied.toUpperCase()) || denied.toUpperCase().includes(normalized)) {
|
|
181
|
+
return {
|
|
182
|
+
compliant: false,
|
|
183
|
+
reason: `License "${license}" is explicitly denied`,
|
|
184
|
+
severity: 'high'
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Check allowed list
|
|
189
|
+
for (const allowed of policy.allowed) {
|
|
190
|
+
if (normalized.includes(allowed.toUpperCase()) || allowed.toUpperCase().includes(normalized)) {
|
|
191
|
+
return { compliant: true };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Handle unknown licenses
|
|
195
|
+
if (normalized === 'UNKNOWN' || normalized === 'UNLICENSED') {
|
|
196
|
+
switch (policy.unknown) {
|
|
197
|
+
case 'allow':
|
|
198
|
+
return { compliant: true };
|
|
199
|
+
case 'deny':
|
|
200
|
+
return {
|
|
201
|
+
compliant: false,
|
|
202
|
+
reason: `Unknown license not allowed by policy`,
|
|
203
|
+
severity: 'high'
|
|
204
|
+
};
|
|
205
|
+
case 'warn':
|
|
206
|
+
default:
|
|
207
|
+
return {
|
|
208
|
+
compliant: false,
|
|
209
|
+
reason: `Unknown license requires manual review`,
|
|
210
|
+
severity: 'low'
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// License not in allowed list
|
|
215
|
+
switch (policy.unknown) {
|
|
216
|
+
case 'allow':
|
|
217
|
+
return { compliant: true };
|
|
218
|
+
case 'deny':
|
|
219
|
+
return {
|
|
220
|
+
compliant: false,
|
|
221
|
+
reason: `License "${license}" is not in the allowed list`,
|
|
222
|
+
severity: 'medium'
|
|
223
|
+
};
|
|
224
|
+
case 'warn':
|
|
225
|
+
default:
|
|
226
|
+
return {
|
|
227
|
+
compliant: false,
|
|
228
|
+
reason: `License "${license}" requires review (not in allowed list)`,
|
|
229
|
+
severity: 'low'
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
export function checkLicenses(targetPath, options = {}) {
|
|
234
|
+
// Determine policy
|
|
235
|
+
let policy;
|
|
236
|
+
if (typeof options.policy === 'string') {
|
|
237
|
+
policy = POLICIES[options.policy] || POLICIES.permissive;
|
|
238
|
+
}
|
|
239
|
+
else if (options.policy) {
|
|
240
|
+
policy = options.policy;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
policy = { ...POLICIES.permissive };
|
|
244
|
+
}
|
|
245
|
+
// Apply custom allowed/denied overrides
|
|
246
|
+
if (options.allowedLicenses) {
|
|
247
|
+
policy = { ...policy, allowed: [...policy.allowed, ...options.allowedLicenses] };
|
|
248
|
+
}
|
|
249
|
+
if (options.deniedLicenses) {
|
|
250
|
+
policy = { ...policy, denied: [...policy.denied, ...options.deniedLicenses] };
|
|
251
|
+
}
|
|
252
|
+
// Collect licenses from all sources
|
|
253
|
+
const licenses = [
|
|
254
|
+
...runLicenseChecker(targetPath),
|
|
255
|
+
...runPipLicenses(targetPath)
|
|
256
|
+
];
|
|
257
|
+
// Check each license
|
|
258
|
+
const violations = [];
|
|
259
|
+
let compliant = 0;
|
|
260
|
+
let unknown = 0;
|
|
261
|
+
for (const info of licenses) {
|
|
262
|
+
const result = checkLicense(info.license, policy);
|
|
263
|
+
if (result.compliant) {
|
|
264
|
+
compliant++;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
if (info.license === 'UNKNOWN') {
|
|
268
|
+
unknown++;
|
|
269
|
+
}
|
|
270
|
+
violations.push({
|
|
271
|
+
package: info.package,
|
|
272
|
+
version: info.version,
|
|
273
|
+
license: info.license,
|
|
274
|
+
violation: result.reason || 'License not compliant',
|
|
275
|
+
severity: result.severity || 'medium'
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
policy: policy.name,
|
|
281
|
+
licenses,
|
|
282
|
+
violations,
|
|
283
|
+
summary: {
|
|
284
|
+
total: licenses.length,
|
|
285
|
+
compliant,
|
|
286
|
+
violations: violations.length,
|
|
287
|
+
unknown
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// Export policy names for CLI
|
|
292
|
+
export const POLICY_NAMES = Object.keys(POLICIES);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database for aurasecurity
|
|
3
|
+
*
|
|
4
|
+
* Provides persistent storage for:
|
|
5
|
+
* - Audit history
|
|
6
|
+
* - Configuration settings
|
|
7
|
+
* - Scan results
|
|
8
|
+
* - Notification history
|
|
9
|
+
*/
|
|
10
|
+
import type { AuditorOutput } from '../types/events.js';
|
|
11
|
+
import type { LocalScanResult } from '../integrations/local-scanner.js';
|
|
12
|
+
import type { AWSScanResult } from '../integrations/aws-scanner.js';
|
|
13
|
+
export interface AuditRecord {
|
|
14
|
+
id: string;
|
|
15
|
+
type: 'code' | 'aws' | 'audit';
|
|
16
|
+
timestamp: string;
|
|
17
|
+
target: string;
|
|
18
|
+
summary: {
|
|
19
|
+
critical: number;
|
|
20
|
+
high: number;
|
|
21
|
+
medium: number;
|
|
22
|
+
low: number;
|
|
23
|
+
};
|
|
24
|
+
data: string;
|
|
25
|
+
}
|
|
26
|
+
export interface SettingsRecord {
|
|
27
|
+
key: string;
|
|
28
|
+
value: string;
|
|
29
|
+
updated_at: string;
|
|
30
|
+
}
|
|
31
|
+
export interface NotificationRecord {
|
|
32
|
+
id: number;
|
|
33
|
+
type: 'slack' | 'discord' | 'webhook';
|
|
34
|
+
audit_id: string;
|
|
35
|
+
status: 'sent' | 'failed' | 'pending';
|
|
36
|
+
message: string;
|
|
37
|
+
timestamp: string;
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare class AuditorDatabase {
|
|
41
|
+
private db;
|
|
42
|
+
private dbPath;
|
|
43
|
+
constructor(dbPath?: string);
|
|
44
|
+
private initTables;
|
|
45
|
+
saveAudit(type: 'code' | 'aws' | 'audit', target: string, result: LocalScanResult | AWSScanResult | AuditorOutput): string;
|
|
46
|
+
getAudit(id: string): AuditRecord | null;
|
|
47
|
+
getAudits(limit?: number, offset?: number, type?: string): AuditRecord[];
|
|
48
|
+
getAuditCount(type?: string): number;
|
|
49
|
+
deleteAudit(id: string): boolean;
|
|
50
|
+
getSetting(key: string): string | null;
|
|
51
|
+
getSettings(prefix?: string): Record<string, string>;
|
|
52
|
+
getAllSettings(): Record<string, string>;
|
|
53
|
+
setSetting(key: string, value: string): void;
|
|
54
|
+
setSettings(settings: Record<string, string>): void;
|
|
55
|
+
saveNotification(type: 'slack' | 'discord' | 'webhook', auditId: string, status: 'sent' | 'failed' | 'pending', message: string, error?: string): number;
|
|
56
|
+
recordNotification(auditId: string, channels: string, success: boolean, error?: string): void;
|
|
57
|
+
getNotifications(auditId?: string, limit?: number): NotificationRecord[];
|
|
58
|
+
getStats(): {
|
|
59
|
+
totalAudits: number;
|
|
60
|
+
byType: Record<string, number>;
|
|
61
|
+
byDay: Array<{
|
|
62
|
+
date: string;
|
|
63
|
+
count: number;
|
|
64
|
+
}>;
|
|
65
|
+
severityCounts: {
|
|
66
|
+
critical: number;
|
|
67
|
+
high: number;
|
|
68
|
+
medium: number;
|
|
69
|
+
low: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
close(): void;
|
|
73
|
+
vacuum(): void;
|
|
74
|
+
deleteOldAudits(daysToKeep?: number): number;
|
|
75
|
+
}
|
|
76
|
+
export declare function getDatabase(dbPath?: string): AuditorDatabase;
|
|
77
|
+
export declare function closeDatabase(): void;
|