guardrail-compliance 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/emitter.d.ts +97 -0
- package/dist/audit/emitter.d.ts.map +1 -0
- package/dist/audit/emitter.js +197 -0
- package/dist/audit/events.d.ts +304 -0
- package/dist/audit/events.d.ts.map +1 -0
- package/dist/audit/events.js +267 -0
- package/dist/audit/index.d.ts +11 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +51 -0
- package/dist/audit/storage.d.ts +93 -0
- package/dist/audit/storage.d.ts.map +1 -0
- package/dist/audit/storage.js +337 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts +2 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts.map +1 -0
- package/dist/automation/__tests__/compliance-scheduler.test.js +140 -0
- package/dist/automation/audit-logger.d.ts +129 -0
- package/dist/automation/audit-logger.d.ts.map +1 -0
- package/dist/automation/audit-logger.js +473 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts +1 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler-fixed.js +1 -0
- package/dist/automation/compliance-scheduler.d.ts +83 -0
- package/dist/automation/compliance-scheduler.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler.js +414 -0
- package/dist/automation/dashboard.d.ts +194 -0
- package/dist/automation/dashboard.d.ts.map +1 -0
- package/dist/automation/dashboard.js +768 -0
- package/dist/automation/email-service.d.ts +69 -0
- package/dist/automation/email-service.d.ts.map +1 -0
- package/dist/automation/email-service.js +218 -0
- package/dist/automation/evidence-collector.d.ts +140 -0
- package/dist/automation/evidence-collector.d.ts.map +1 -0
- package/dist/automation/evidence-collector.js +682 -0
- package/dist/automation/index.d.ts +8 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js +24 -0
- package/dist/automation/pdf-exporter.d.ts +90 -0
- package/dist/automation/pdf-exporter.d.ts.map +1 -0
- package/dist/automation/pdf-exporter.js +381 -0
- package/dist/automation/reporting-engine.d.ts +116 -0
- package/dist/automation/reporting-engine.d.ts.map +1 -0
- package/dist/automation/reporting-engine.js +329 -0
- package/dist/container/index.d.ts +4 -0
- package/dist/container/index.d.ts.map +1 -0
- package/dist/container/index.js +19 -0
- package/dist/container/kubernetes.d.ts +94 -0
- package/dist/container/kubernetes.d.ts.map +1 -0
- package/dist/container/kubernetes.js +268 -0
- package/dist/container/rules.d.ts +27 -0
- package/dist/container/rules.d.ts.map +1 -0
- package/dist/container/rules.js +216 -0
- package/dist/container/scanner.d.ts +50 -0
- package/dist/container/scanner.d.ts.map +1 -0
- package/dist/container/scanner.js +143 -0
- package/dist/frameworks/engine.d.ts +108 -0
- package/dist/frameworks/engine.d.ts.map +1 -0
- package/dist/frameworks/engine.js +206 -0
- package/dist/frameworks/gdpr.d.ts +6 -0
- package/dist/frameworks/gdpr.d.ts.map +1 -0
- package/dist/frameworks/gdpr.js +198 -0
- package/dist/frameworks/hipaa.d.ts +6 -0
- package/dist/frameworks/hipaa.d.ts.map +1 -0
- package/dist/frameworks/hipaa.js +183 -0
- package/dist/frameworks/index.d.ts +8 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/iso27001.d.ts +63 -0
- package/dist/frameworks/iso27001.d.ts.map +1 -0
- package/dist/frameworks/iso27001.js +331 -0
- package/dist/frameworks/nist.d.ts +62 -0
- package/dist/frameworks/nist.d.ts.map +1 -0
- package/dist/frameworks/nist.js +424 -0
- package/dist/frameworks/pci.d.ts +6 -0
- package/dist/frameworks/pci.d.ts.map +1 -0
- package/dist/frameworks/pci.js +201 -0
- package/dist/frameworks/soc2.d.ts +7 -0
- package/dist/frameworks/soc2.d.ts.map +1 -0
- package/dist/frameworks/soc2.js +248 -0
- package/dist/iac/drift-detector.d.ts +64 -0
- package/dist/iac/drift-detector.d.ts.map +1 -0
- package/dist/iac/drift-detector.js +134 -0
- package/dist/iac/index.d.ts +4 -0
- package/dist/iac/index.d.ts.map +1 -0
- package/dist/iac/index.js +19 -0
- package/dist/iac/rules.d.ts +17 -0
- package/dist/iac/rules.d.ts.map +1 -0
- package/dist/iac/rules.js +385 -0
- package/dist/iac/scanner.d.ts +104 -0
- package/dist/iac/scanner.d.ts.map +1 -0
- package/dist/iac/scanner.js +343 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/pii/data-flow.d.ts +58 -0
- package/dist/pii/data-flow.d.ts.map +1 -0
- package/dist/pii/data-flow.js +154 -0
- package/dist/pii/detector.d.ts +60 -0
- package/dist/pii/detector.d.ts.map +1 -0
- package/dist/pii/detector.js +267 -0
- package/dist/pii/index.d.ts +4 -0
- package/dist/pii/index.d.ts.map +1 -0
- package/dist/pii/index.js +19 -0
- package/dist/pii/patterns.d.ts +36 -0
- package/dist/pii/patterns.d.ts.map +1 -0
- package/dist/pii/patterns.js +108 -0
- package/dist/policy/index.d.ts +5 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +20 -0
- package/dist/policy/opa-engine.d.ts +121 -0
- package/dist/policy/opa-engine.d.ts.map +1 -0
- package/dist/policy/opa-engine.js +423 -0
- package/package.json +31 -0
- package/src/audit/emitter.ts +383 -0
- package/src/audit/events.ts +351 -0
- package/src/audit/index.ts +35 -0
- package/src/audit/storage.ts +394 -0
- package/src/automation/__tests__/compliance-scheduler.test.ts +183 -0
- package/src/automation/audit-logger.ts +629 -0
- package/src/automation/compliance-scheduler-fixed.ts +0 -0
- package/src/automation/compliance-scheduler.ts +516 -0
- package/src/automation/dashboard.ts +947 -0
- package/src/automation/email-service.ts +230 -0
- package/src/automation/evidence-collector.ts +866 -0
- package/src/automation/index.ts +8 -0
- package/src/automation/pdf-exporter.ts +434 -0
- package/src/automation/reporting-engine.ts +462 -0
- package/src/container/index.ts +3 -0
- package/src/container/kubernetes.ts +379 -0
- package/src/container/rules.ts +244 -0
- package/src/container/scanner.ts +202 -0
- package/src/frameworks/engine.ts +298 -0
- package/src/frameworks/gdpr.ts +204 -0
- package/src/frameworks/hipaa.ts +209 -0
- package/src/frameworks/index.ts +23 -0
- package/src/frameworks/iso27001.ts +398 -0
- package/src/frameworks/nist.ts +518 -0
- package/src/frameworks/pci.ts +226 -0
- package/src/frameworks/soc2.ts +281 -0
- package/src/iac/drift-detector.ts +197 -0
- package/src/iac/index.ts +3 -0
- package/src/iac/rules.ts +420 -0
- package/src/iac/scanner.ts +445 -0
- package/src/index.ts +17 -0
- package/src/pii/data-flow.ts +216 -0
- package/src/pii/detector.ts +327 -0
- package/src/pii/index.ts +3 -0
- package/src/pii/patterns.ts +128 -0
- package/src/policy/index.ts +5 -0
- package/src/policy/opa-engine.ts +504 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.iacSecurityScanner = exports.IaCSecurityScanner = void 0;
|
|
4
|
+
const database_1 = require("@guardrail/database");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const rules_1 = require("./rules");
|
|
8
|
+
const drift_detector_1 = require("./drift-detector");
|
|
9
|
+
class IaCSecurityScanner {
|
|
10
|
+
/**
|
|
11
|
+
* Main scan function
|
|
12
|
+
*/
|
|
13
|
+
async scan(projectPath, projectId) {
|
|
14
|
+
const providers = [];
|
|
15
|
+
const allResources = [];
|
|
16
|
+
// Find and parse Terraform files
|
|
17
|
+
const tfFiles = this.findFiles(projectPath, '.tf');
|
|
18
|
+
if (tfFiles.length > 0) {
|
|
19
|
+
providers.push('terraform');
|
|
20
|
+
for (const file of tfFiles) {
|
|
21
|
+
const resources = await this.parseTerraform([file]);
|
|
22
|
+
resources.forEach(r => allResources.push({ resource: r, filePath: file, provider: 'terraform' }));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Find and parse CloudFormation templates
|
|
26
|
+
const cfnFiles = this.findFiles(projectPath, '.yaml', '.yml', '.json').filter(f => f.includes('cloudformation') || f.includes('template'));
|
|
27
|
+
if (cfnFiles.length > 0) {
|
|
28
|
+
providers.push('cloudformation');
|
|
29
|
+
for (const file of cfnFiles) {
|
|
30
|
+
const resources = await this.parseCloudFormation(file);
|
|
31
|
+
resources.forEach(r => allResources.push({ resource: r, filePath: file, provider: 'cloudformation' }));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Find and parse Kubernetes manifests
|
|
35
|
+
const k8sFiles = this.findFiles(projectPath, '.yaml', '.yml').filter(f => !f.includes('cloudformation') && !f.includes('template'));
|
|
36
|
+
if (k8sFiles.length > 0) {
|
|
37
|
+
const k8sResources = await this.parseKubernetes(k8sFiles);
|
|
38
|
+
if (k8sResources.length > 0) {
|
|
39
|
+
providers.push('kubernetes');
|
|
40
|
+
k8sResources.forEach((r, i) => allResources.push({ resource: r, filePath: k8sFiles[i % k8sFiles.length] || 'unknown', provider: 'kubernetes' }));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Scan all resources against rules
|
|
44
|
+
const findings = this.scanResources(allResources);
|
|
45
|
+
// Calculate summary
|
|
46
|
+
const summary = {
|
|
47
|
+
total: findings.length,
|
|
48
|
+
critical: findings.filter(f => f.severity === 'critical').length,
|
|
49
|
+
high: findings.filter(f => f.severity === 'high').length,
|
|
50
|
+
medium: findings.filter(f => f.severity === 'medium').length,
|
|
51
|
+
low: findings.filter(f => f.severity === 'low').length
|
|
52
|
+
};
|
|
53
|
+
// Detect drift if Terraform is used
|
|
54
|
+
let driftReport;
|
|
55
|
+
if (providers.includes('terraform')) {
|
|
56
|
+
driftReport = await drift_detector_1.driftDetector.detectDrift(projectPath);
|
|
57
|
+
}
|
|
58
|
+
// Analyze costs
|
|
59
|
+
const costAnalysis = await this.analyzeCosts(findings);
|
|
60
|
+
// Save to database
|
|
61
|
+
try {
|
|
62
|
+
await database_1.prisma.iaCScan.create({
|
|
63
|
+
data: {
|
|
64
|
+
projectId
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Table may not exist - continue
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
projectId,
|
|
73
|
+
providers,
|
|
74
|
+
summary,
|
|
75
|
+
findings,
|
|
76
|
+
driftReport,
|
|
77
|
+
costAnalysis
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parse Terraform files
|
|
82
|
+
*/
|
|
83
|
+
async parseTerraform(files) {
|
|
84
|
+
const resources = [];
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
try {
|
|
87
|
+
const content = (0, fs_1.readFileSync)(file, 'utf-8');
|
|
88
|
+
// Simple HCL parsing (in production, use proper HCL parser)
|
|
89
|
+
const resourceMatches = content.matchAll(/resource\s+"([^"]+)"\s+"([^"]+)"\s+{([^}]*)}/gs);
|
|
90
|
+
for (const match of resourceMatches) {
|
|
91
|
+
const [, type, name, configBlock] = match;
|
|
92
|
+
if (!type || !name)
|
|
93
|
+
continue;
|
|
94
|
+
// Parse config block (simplified)
|
|
95
|
+
const config = {};
|
|
96
|
+
const lines = (configBlock || '').split('\n');
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
const keyValue = line.match(/^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
|
|
99
|
+
if (keyValue) {
|
|
100
|
+
const [, key, value] = keyValue;
|
|
101
|
+
if (key && value) {
|
|
102
|
+
config[key.trim()] = this.parseValue(value.trim());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
resources.push({
|
|
107
|
+
type,
|
|
108
|
+
name,
|
|
109
|
+
provider: type.split('_')[0] || 'unknown', // e.g., 'aws' from 'aws_s3_bucket'
|
|
110
|
+
config
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(`Error parsing Terraform file ${file}:`, error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return resources;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Parse CloudFormation template
|
|
122
|
+
*/
|
|
123
|
+
async parseCloudFormation(file) {
|
|
124
|
+
const resources = [];
|
|
125
|
+
try {
|
|
126
|
+
const content = (0, fs_1.readFileSync)(file, 'utf-8');
|
|
127
|
+
const template = file.endsWith('.json') ? JSON.parse(content) : this.parseYAML(content);
|
|
128
|
+
if (template.Resources) {
|
|
129
|
+
for (const [logicalId, resource] of Object.entries(template.Resources)) {
|
|
130
|
+
resources.push({
|
|
131
|
+
type: resource.Type,
|
|
132
|
+
logicalId,
|
|
133
|
+
properties: resource.Properties || {}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error(`Error parsing CloudFormation file ${file}:`, error);
|
|
140
|
+
}
|
|
141
|
+
return resources;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Parse Kubernetes manifests
|
|
145
|
+
*/
|
|
146
|
+
async parseKubernetes(files) {
|
|
147
|
+
const resources = [];
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
try {
|
|
150
|
+
const content = (0, fs_1.readFileSync)(file, 'utf-8');
|
|
151
|
+
const manifests = this.parseYAML(content);
|
|
152
|
+
// Handle both single manifest and array of manifests
|
|
153
|
+
const manifestArray = Array.isArray(manifests) ? manifests : [manifests];
|
|
154
|
+
for (const manifest of manifestArray) {
|
|
155
|
+
if (manifest && manifest.kind && manifest.metadata) {
|
|
156
|
+
resources.push({
|
|
157
|
+
apiVersion: manifest.apiVersion || '',
|
|
158
|
+
kind: manifest.kind,
|
|
159
|
+
metadata: manifest.metadata,
|
|
160
|
+
spec: manifest.spec || {}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error(`Error parsing Kubernetes file ${file}:`, error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return resources;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Scan resources against rules
|
|
173
|
+
*/
|
|
174
|
+
scanResources(resources) {
|
|
175
|
+
const findings = [];
|
|
176
|
+
for (const { resource, filePath, provider } of resources) {
|
|
177
|
+
// Get applicable rules
|
|
178
|
+
const rules = rules_1.ALL_RULES.filter(rule => {
|
|
179
|
+
if (provider === 'terraform') {
|
|
180
|
+
const tfResource = resource;
|
|
181
|
+
return rule.provider === 'terraform' && rule.resourceType === tfResource.type;
|
|
182
|
+
}
|
|
183
|
+
else if (provider === 'kubernetes') {
|
|
184
|
+
const k8sResource = resource;
|
|
185
|
+
return rule.provider === 'kubernetes' && rule.resourceType === k8sResource.kind;
|
|
186
|
+
}
|
|
187
|
+
else if (provider === 'cloudformation') {
|
|
188
|
+
const cfnResource = resource;
|
|
189
|
+
return rule.resourceType === cfnResource.type;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
});
|
|
193
|
+
// Check each rule
|
|
194
|
+
for (const rule of rules) {
|
|
195
|
+
try {
|
|
196
|
+
const resourceData = this.getResourceData(resource, provider);
|
|
197
|
+
const violates = rule.check(resourceData);
|
|
198
|
+
if (violates) {
|
|
199
|
+
findings.push({
|
|
200
|
+
ruleId: rule.id,
|
|
201
|
+
severity: rule.severity,
|
|
202
|
+
category: rule.category,
|
|
203
|
+
title: rule.title,
|
|
204
|
+
description: rule.description,
|
|
205
|
+
resourceType: this.getResourceType(resource, provider),
|
|
206
|
+
resourceName: this.getResourceName(resource, provider),
|
|
207
|
+
filePath,
|
|
208
|
+
recommendation: rule.recommendation
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error(`Error checking rule ${rule.id}:`, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return findings;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Analyze costs based on findings
|
|
221
|
+
*/
|
|
222
|
+
async analyzeCosts(findings) {
|
|
223
|
+
const costByService = {};
|
|
224
|
+
const suggestions = [];
|
|
225
|
+
// Estimate costs based on security findings
|
|
226
|
+
// In production, this would use cloud provider pricing APIs
|
|
227
|
+
// Example: unencrypted resources cost more due to compliance requirements
|
|
228
|
+
const encryptionFindings = findings.filter(f => f.description.toLowerCase().includes('encryption'));
|
|
229
|
+
if (encryptionFindings.length > 0) {
|
|
230
|
+
suggestions.push(`Enable encryption on ${encryptionFindings.length} resources to reduce compliance costs`);
|
|
231
|
+
}
|
|
232
|
+
// Public resources may incur data transfer costs
|
|
233
|
+
const publicFindings = findings.filter(f => f.description.toLowerCase().includes('public'));
|
|
234
|
+
if (publicFindings.length > 0) {
|
|
235
|
+
suggestions.push(`Restrict ${publicFindings.length} public resources to reduce data transfer costs`);
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
estimatedMonthlyCost: 0, // Would calculate actual costs in production
|
|
239
|
+
costByService,
|
|
240
|
+
costOptimizationSuggestions: suggestions
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Find files with specific extensions recursively
|
|
245
|
+
*/
|
|
246
|
+
findFiles(dir, ...extensions) {
|
|
247
|
+
const files = [];
|
|
248
|
+
try {
|
|
249
|
+
const entries = (0, fs_1.readdirSync)(dir);
|
|
250
|
+
for (const entry of entries) {
|
|
251
|
+
const fullPath = (0, path_1.join)(dir, entry);
|
|
252
|
+
const stat = (0, fs_1.statSync)(fullPath);
|
|
253
|
+
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
|
254
|
+
files.push(...this.findFiles(fullPath, ...extensions));
|
|
255
|
+
}
|
|
256
|
+
else if (stat.isFile()) {
|
|
257
|
+
if (extensions.some(ext => entry.endsWith(ext))) {
|
|
258
|
+
files.push(fullPath);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
// Ignore permission errors
|
|
265
|
+
}
|
|
266
|
+
return files;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Parse HCL value (simplified)
|
|
270
|
+
*/
|
|
271
|
+
parseValue(value) {
|
|
272
|
+
value = value.trim();
|
|
273
|
+
if (value === 'true')
|
|
274
|
+
return true;
|
|
275
|
+
if (value === 'false')
|
|
276
|
+
return false;
|
|
277
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
278
|
+
return value.slice(1, -1);
|
|
279
|
+
}
|
|
280
|
+
if (value.match(/^\d+$/)) {
|
|
281
|
+
return parseInt(value, 10);
|
|
282
|
+
}
|
|
283
|
+
return value;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Simple YAML parser (in production, use proper YAML library)
|
|
287
|
+
*/
|
|
288
|
+
parseYAML(content) {
|
|
289
|
+
try {
|
|
290
|
+
return JSON.parse(content);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return {};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get resource data for rule checking
|
|
298
|
+
*/
|
|
299
|
+
getResourceData(resource, provider) {
|
|
300
|
+
if (provider === 'terraform') {
|
|
301
|
+
return resource.config;
|
|
302
|
+
}
|
|
303
|
+
else if (provider === 'kubernetes') {
|
|
304
|
+
return resource;
|
|
305
|
+
}
|
|
306
|
+
else if (provider === 'cloudformation') {
|
|
307
|
+
return resource.properties;
|
|
308
|
+
}
|
|
309
|
+
return {};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Get resource type
|
|
313
|
+
*/
|
|
314
|
+
getResourceType(resource, provider) {
|
|
315
|
+
if (provider === 'terraform') {
|
|
316
|
+
return resource.type;
|
|
317
|
+
}
|
|
318
|
+
else if (provider === 'kubernetes') {
|
|
319
|
+
return resource.kind;
|
|
320
|
+
}
|
|
321
|
+
else if (provider === 'cloudformation') {
|
|
322
|
+
return resource.type;
|
|
323
|
+
}
|
|
324
|
+
return 'unknown';
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get resource name
|
|
328
|
+
*/
|
|
329
|
+
getResourceName(resource, provider) {
|
|
330
|
+
if (provider === 'terraform') {
|
|
331
|
+
return resource.name;
|
|
332
|
+
}
|
|
333
|
+
else if (provider === 'kubernetes') {
|
|
334
|
+
return resource.metadata.name;
|
|
335
|
+
}
|
|
336
|
+
else if (provider === 'cloudformation') {
|
|
337
|
+
return resource.logicalId;
|
|
338
|
+
}
|
|
339
|
+
return 'unknown';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.IaCSecurityScanner = IaCSecurityScanner;
|
|
343
|
+
exports.iacSecurityScanner = new IaCSecurityScanner();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,OAAO,CAAC;AAGtB,cAAc,cAAc,CAAC;AAG7B,cAAc,OAAO,CAAC;AAGtB,cAAc,aAAa,CAAC;AAG5B,cAAc,cAAc,CAAC;AAG7B,cAAc,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// IaC Security
|
|
18
|
+
__exportStar(require("./iac"), exports);
|
|
19
|
+
// Compliance Frameworks
|
|
20
|
+
__exportStar(require("./frameworks"), exports);
|
|
21
|
+
// PII Detection
|
|
22
|
+
__exportStar(require("./pii"), exports);
|
|
23
|
+
// Container Security
|
|
24
|
+
__exportStar(require("./container"), exports);
|
|
25
|
+
// Automation Components
|
|
26
|
+
__exportStar(require("./automation"), exports);
|
|
27
|
+
// Audit Trail (Compliance+ feature)
|
|
28
|
+
__exportStar(require("./audit"), exports);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface PIIFinding {
|
|
2
|
+
category: string;
|
|
3
|
+
value: string;
|
|
4
|
+
location: {
|
|
5
|
+
file: string;
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
};
|
|
9
|
+
context: string;
|
|
10
|
+
severity: 'high' | 'medium' | 'low';
|
|
11
|
+
}
|
|
12
|
+
export interface Origin {
|
|
13
|
+
type: 'input' | 'database' | 'api' | 'file' | 'environment';
|
|
14
|
+
location: string;
|
|
15
|
+
description: string;
|
|
16
|
+
}
|
|
17
|
+
export interface FlowPath {
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
operation: string;
|
|
21
|
+
line: number;
|
|
22
|
+
}
|
|
23
|
+
export interface Storage {
|
|
24
|
+
type: 'database' | 'file' | 'memory' | 'cache' | 'external';
|
|
25
|
+
location: string;
|
|
26
|
+
encrypted: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface DataFlow {
|
|
29
|
+
piiCategory: string;
|
|
30
|
+
origins: Origin[];
|
|
31
|
+
paths: FlowPath[];
|
|
32
|
+
storage: Storage[];
|
|
33
|
+
externalTransfers: string[];
|
|
34
|
+
}
|
|
35
|
+
export declare class DataFlowTracker {
|
|
36
|
+
/**
|
|
37
|
+
* Track where PII originates
|
|
38
|
+
*/
|
|
39
|
+
trackOrigin(_finding: PIIFinding, _ast: any): Origin[];
|
|
40
|
+
/**
|
|
41
|
+
* Track where PII flows through the codebase
|
|
42
|
+
*/
|
|
43
|
+
trackFlow(_finding: PIIFinding, _ast: any): FlowPath[];
|
|
44
|
+
/**
|
|
45
|
+
* Track where PII is stored
|
|
46
|
+
*/
|
|
47
|
+
trackStorage(_finding: PIIFinding, _ast: any): Storage[];
|
|
48
|
+
/**
|
|
49
|
+
* Generate Mermaid diagram for data flows
|
|
50
|
+
*/
|
|
51
|
+
generateDiagram(flows: DataFlow[]): string;
|
|
52
|
+
/**
|
|
53
|
+
* Analyze data flows for a project
|
|
54
|
+
*/
|
|
55
|
+
analyzeDataFlows(findings: PIIFinding[]): DataFlow[];
|
|
56
|
+
}
|
|
57
|
+
export declare const dataFlowTracker: DataFlowTracker;
|
|
58
|
+
//# sourceMappingURL=data-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-flow.d.ts","sourceRoot":"","sources":["../../src/pii/data-flow.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACrC;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,aAAa,CAAC;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,qBAAa,eAAe;IAC1B;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,EAAE;IAoDtD;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,GAAG,QAAQ,EAAE;IAStD;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,EAAE;IAiCxD;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM;IA6B1C;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,QAAQ,EAAE;CAmCrD;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dataFlowTracker = exports.DataFlowTracker = void 0;
|
|
4
|
+
class DataFlowTracker {
|
|
5
|
+
/**
|
|
6
|
+
* Track where PII originates
|
|
7
|
+
*/
|
|
8
|
+
trackOrigin(_finding, _ast) {
|
|
9
|
+
const origins = [];
|
|
10
|
+
// Simplified - in production would analyze AST deeply
|
|
11
|
+
// Check if in request handler
|
|
12
|
+
if (_finding.context.includes('req.body') || _finding.context.includes('request')) {
|
|
13
|
+
origins.push({
|
|
14
|
+
type: 'input',
|
|
15
|
+
location: _finding.location.file,
|
|
16
|
+
description: 'User input from HTTP request'
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
// Check if from database query
|
|
20
|
+
if (_finding.context.includes('db.') || _finding.context.includes('query') || _finding.context.includes('findMany')) {
|
|
21
|
+
origins.push({
|
|
22
|
+
type: 'database',
|
|
23
|
+
location: _finding.location.file,
|
|
24
|
+
description: 'Retrieved from database'
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Check if from API call
|
|
28
|
+
if (_finding.context.includes('fetch') || _finding.context.includes('axios') || _finding.context.includes('http.get')) {
|
|
29
|
+
origins.push({
|
|
30
|
+
type: 'api',
|
|
31
|
+
location: _finding.location.file,
|
|
32
|
+
description: 'Fetched from external API'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Check if from file
|
|
36
|
+
if (_finding.context.includes('readFile') || _finding.context.includes('fs.')) {
|
|
37
|
+
origins.push({
|
|
38
|
+
type: 'file',
|
|
39
|
+
location: _finding.location.file,
|
|
40
|
+
description: 'Read from file system'
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Check if from environment
|
|
44
|
+
if (_finding.context.includes('process.env') || _finding.context.includes('env.')) {
|
|
45
|
+
origins.push({
|
|
46
|
+
type: 'environment',
|
|
47
|
+
location: 'Environment variables',
|
|
48
|
+
description: 'Loaded from environment variables'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return origins;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Track where PII flows through the codebase
|
|
55
|
+
*/
|
|
56
|
+
trackFlow(_finding, _ast) {
|
|
57
|
+
const paths = [];
|
|
58
|
+
// Simplified - would analyze actual data flow in AST
|
|
59
|
+
// This is a placeholder implementation
|
|
60
|
+
return paths;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Track where PII is stored
|
|
64
|
+
*/
|
|
65
|
+
trackStorage(_finding, _ast) {
|
|
66
|
+
const storage = [];
|
|
67
|
+
// Check for database storage
|
|
68
|
+
if (_finding.context.includes('save') || _finding.context.includes('insert') || _finding.context.includes('create')) {
|
|
69
|
+
storage.push({
|
|
70
|
+
type: 'database',
|
|
71
|
+
location: 'Database (inferred)',
|
|
72
|
+
encrypted: false // Would check encryption settings
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Check for file storage
|
|
76
|
+
if (_finding.context.includes('writeFile') || _finding.context.includes('fs.write')) {
|
|
77
|
+
storage.push({
|
|
78
|
+
type: 'file',
|
|
79
|
+
location: _finding.location.file,
|
|
80
|
+
encrypted: false
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Check for cache storage
|
|
84
|
+
if (_finding.context.includes('cache') || _finding.context.includes('redis')) {
|
|
85
|
+
storage.push({
|
|
86
|
+
type: 'cache',
|
|
87
|
+
location: 'Cache (inferred)',
|
|
88
|
+
encrypted: false
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return storage;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generate Mermaid diagram for data flows
|
|
95
|
+
*/
|
|
96
|
+
generateDiagram(flows) {
|
|
97
|
+
let diagram = 'graph LR\n';
|
|
98
|
+
for (const flow of flows) {
|
|
99
|
+
const piiNode = `PII_${flow.piiCategory.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
|
100
|
+
// Origins
|
|
101
|
+
for (const origin of flow.origins) {
|
|
102
|
+
const originNode = `${origin.type}_${flow.piiCategory}`;
|
|
103
|
+
diagram += ` ${originNode}[${origin.type}: ${origin.description}] --> ${piiNode}[${flow.piiCategory}]\n`;
|
|
104
|
+
}
|
|
105
|
+
// Storage
|
|
106
|
+
for (const store of flow.storage) {
|
|
107
|
+
const storeNode = `${store.type}_${flow.piiCategory}`;
|
|
108
|
+
const encrypted = store.encrypted ? '🔒' : '🔓';
|
|
109
|
+
diagram += ` ${piiNode} --> ${storeNode}[${encrypted} ${store.type}]\n`;
|
|
110
|
+
}
|
|
111
|
+
// External transfers
|
|
112
|
+
for (const transfer of flow.externalTransfers) {
|
|
113
|
+
const transferNode = `EXT_${transfer.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
|
114
|
+
diagram += ` ${piiNode} --> ${transferNode}[External: ${transfer}]\n`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return diagram;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Analyze data flows for a project
|
|
121
|
+
*/
|
|
122
|
+
analyzeDataFlows(findings) {
|
|
123
|
+
const flowsByCategory = new Map();
|
|
124
|
+
for (const finding of findings) {
|
|
125
|
+
if (!flowsByCategory.has(finding.category)) {
|
|
126
|
+
flowsByCategory.set(finding.category, {
|
|
127
|
+
piiCategory: finding.category,
|
|
128
|
+
origins: [],
|
|
129
|
+
paths: [],
|
|
130
|
+
storage: [],
|
|
131
|
+
externalTransfers: []
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const flow = flowsByCategory.get(finding.category);
|
|
135
|
+
// Track origins (simplified without AST)
|
|
136
|
+
const origins = this.trackOrigin(finding, null);
|
|
137
|
+
for (const origin of origins) {
|
|
138
|
+
if (!flow.origins.some(o => o.type === origin.type && o.location === origin.location)) {
|
|
139
|
+
flow.origins.push(origin);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Track storage (simplified without AST)
|
|
143
|
+
const storage = this.trackStorage(finding, null);
|
|
144
|
+
for (const store of storage) {
|
|
145
|
+
if (!flow.storage.some(s => s.type === store.type && s.location === store.location)) {
|
|
146
|
+
flow.storage.push(store);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return Array.from(flowsByCategory.values());
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.DataFlowTracker = DataFlowTracker;
|
|
154
|
+
exports.dataFlowTracker = new DataFlowTracker();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { PIIFinding, DataFlow } from './data-flow';
|
|
2
|
+
export interface PIIDetectionResult {
|
|
3
|
+
projectId: string;
|
|
4
|
+
summary: {
|
|
5
|
+
totalFindings: number;
|
|
6
|
+
byCategory: Record<string, number>;
|
|
7
|
+
riskLevel: 'high' | 'medium' | 'low';
|
|
8
|
+
};
|
|
9
|
+
findings: PIIFinding[];
|
|
10
|
+
dataFlows: DataFlow[];
|
|
11
|
+
recommendations: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare class PIIDetector {
|
|
14
|
+
/**
|
|
15
|
+
* Detect PII in entire project
|
|
16
|
+
*/
|
|
17
|
+
detectPII(projectPath: string, projectId: string): Promise<PIIDetectionResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Scan file content for PII patterns
|
|
20
|
+
*/
|
|
21
|
+
private scanContent;
|
|
22
|
+
/**
|
|
23
|
+
* Scan AST for PII field names
|
|
24
|
+
*/
|
|
25
|
+
private scanFieldNames;
|
|
26
|
+
/**
|
|
27
|
+
* Track data flows for PII
|
|
28
|
+
*/
|
|
29
|
+
private trackPIIDataFlows;
|
|
30
|
+
/**
|
|
31
|
+
* Generate recommendations based on findings
|
|
32
|
+
*/
|
|
33
|
+
private generateRecommendations;
|
|
34
|
+
/**
|
|
35
|
+
* Find source files in project
|
|
36
|
+
*/
|
|
37
|
+
private findSourceFiles;
|
|
38
|
+
/**
|
|
39
|
+
* Check if value is a test value
|
|
40
|
+
*/
|
|
41
|
+
private isTestValue;
|
|
42
|
+
/**
|
|
43
|
+
* Check if context indicates non-PII usage
|
|
44
|
+
*/
|
|
45
|
+
private isNonPIIContext;
|
|
46
|
+
/**
|
|
47
|
+
* Get line and column from position
|
|
48
|
+
*/
|
|
49
|
+
private getLineAndColumn;
|
|
50
|
+
/**
|
|
51
|
+
* Get context around a line
|
|
52
|
+
*/
|
|
53
|
+
private getContext;
|
|
54
|
+
/**
|
|
55
|
+
* Mask sensitive value
|
|
56
|
+
*/
|
|
57
|
+
private maskValue;
|
|
58
|
+
}
|
|
59
|
+
export declare const piiDetector: PIIDetector;
|
|
60
|
+
//# sourceMappingURL=detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/pii/detector.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;KACtC,CAAC;IACF,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,qBAAa,WAAW;IACtB;;OAEG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgEpF;;OAEG;IACH,OAAO,CAAC,WAAW;IA6CnB;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;YACW,iBAAiB;IAK/B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA4C/B;;OAEG;IACH,OAAO,CAAC,eAAe;IA0BvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,UAAU;IAMlB;;OAEG;IACH,OAAO,CAAC,SAAS;CAqBlB;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|