Skip to content

Commit 1ae4c54

Browse files
committed
feat(logging): add checks for .log and .json files
1 parent d64015d commit 1ae4c54

File tree

6 files changed

+276
-0
lines changed

6 files changed

+276
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
const fs = require('fs');
2+
const csv = require('csv-parser');
3+
const XLSX = require('xlsx');
4+
const path = require('path');
5+
const { exec: getDiffExec } = require('./getDiff');
6+
7+
// Function to check for sensitive data patterns
8+
const checkForSensitiveData = (cell) => {
9+
const sensitivePatterns = [
10+
/\d{3}-\d{2}-\d{4}/, // Social Security Number (SSN)
11+
/\b\d{16}\b/, // Credit card numbers
12+
/\b\d{5}-\d{4}\b/, // ZIP+4 codes
13+
// Add more patterns as needed
14+
];
15+
return sensitivePatterns.some(pattern => {
16+
if (pattern.test(String(cell))) {
17+
console.log(`\x1b[31mDetected sensitive data: ${cell}\x1b[0m`); // Log the detected sensitive data in red
18+
return true;
19+
}
20+
return false;
21+
});
22+
};
23+
24+
// Function to process CSV files
25+
const processCSV = async (filePath) => {
26+
return new Promise((resolve, reject) => {
27+
let sensitiveDataFound = false;
28+
29+
fs.createReadStream(filePath)
30+
.pipe(csv())
31+
.on('data', (row) => {
32+
for (const [key, value] of Object.entries(row)) {
33+
if (checkForSensitiveData(value)) {
34+
console.log(`\x1b[33mSensitive data found in CSV: ${key}: ${value}\x1b[0m`); // Log in yellow
35+
sensitiveDataFound = true;
36+
}
37+
}
38+
})
39+
.on('end', () => {
40+
if (!sensitiveDataFound) {
41+
console.log('No sensitive data found in CSV.');
42+
}
43+
resolve(sensitiveDataFound); // Resolve with the flag indicating if sensitive data was found
44+
})
45+
.on('error', (err) => {
46+
console.error(`Error reading CSV file: ${err.message}`);
47+
reject(err); // Reject the promise on error
48+
});
49+
});
50+
};
51+
52+
// Function to process XLSX files
53+
const processXLSX = async (filePath) => {
54+
return new Promise((resolve, reject) => {
55+
let sensitiveDataFound = false;
56+
57+
try {
58+
const workbook = XLSX.readFile(filePath);
59+
const sheetName = workbook.SheetNames[0];
60+
const sheet = workbook.Sheets[sheetName];
61+
const jsonData = XLSX.utils.sheet_to_json(sheet);
62+
63+
jsonData.forEach((row) => {
64+
for (const [key, value] of Object.entries(row)) {
65+
if (checkForSensitiveData(value)) {
66+
console.log(`\x1b[33mSensitive data found in XLSX: ${key}: ${value}\x1b[0m`); // Log in yellow
67+
sensitiveDataFound = true;
68+
}
69+
}
70+
});
71+
72+
if (!sensitiveDataFound) {
73+
console.log('No sensitive data found in XLSX.');
74+
}
75+
resolve(sensitiveDataFound); // Resolve with the flag indicating if sensitive data was found
76+
} catch (error) {
77+
console.error(`Error reading XLSX file: ${error.message}`);
78+
reject(error); // Reject the promise on error
79+
}
80+
});
81+
};
82+
83+
// Function to check for sensitive data in .log and .json files
84+
const checkLogJsonFiles = async (filePath) => {
85+
return new Promise((resolve, reject) => {
86+
let sensitiveDataFound = false;
87+
88+
fs.readFile(filePath, 'utf8', (err, data) => {
89+
if (err) {
90+
console.error(`Error reading file ${filePath}: ${err.message}`);
91+
return reject(err);
92+
}
93+
94+
if (checkForSensitiveData(data)) {
95+
console.log(`\x1b[33mSensitive data found in ${filePath}\x1b[0m`);
96+
sensitiveDataFound = true;
97+
}
98+
99+
resolve(sensitiveDataFound);
100+
});
101+
});
102+
};
103+
104+
// Function to parse the file based on its extension
105+
const parseFile = async (filePath) => {
106+
const ext = path.extname(filePath).toLowerCase();
107+
108+
switch (ext) {
109+
case '.csv':
110+
return await processCSV(filePath);
111+
case '.xlsx':
112+
return await processXLSX(filePath);
113+
case '.log':
114+
return await checkLogJsonFiles(filePath);
115+
case '.json':
116+
return await checkLogJsonFiles(filePath);
117+
default:
118+
// Skip unsupported file types without logging
119+
return false; // Indicate that no sensitive data was found for unsupported types
120+
}
121+
};
122+
123+
// Async exec function to handle actions
124+
const exec = async (req, action) => {
125+
// getDiffExec(req, action); // Call to getDiffExec if necessary
126+
127+
const diffStep = action.steps.find((s) => s.stepName === 'diff');
128+
129+
if (diffStep && diffStep.content) {
130+
console.log('Diff content:', diffStep.content);
131+
132+
const filePaths = diffStep.content.filePaths || [];
133+
134+
if (filePaths.length > 0) {
135+
// Check for sensitive data in all files
136+
const sensitiveDataFound = await Promise.all(filePaths.map(parseFile));
137+
const anySensitiveDataDetected = sensitiveDataFound.some(found => found); // Check if any file reported sensitive data
138+
139+
if (anySensitiveDataDetected) {
140+
action.pushBlocked = true; // Block the push
141+
action.error = true; // Set error flag
142+
action.errorMessage = 'Your push has been blocked due to sensitive data detection.'; // Set error message
143+
console.log(action.errorMessage);
144+
}
145+
} else {
146+
console.log('No file paths provided in the diff step.');
147+
}
148+
} else {
149+
console.log('No diff content available.');
150+
}
151+
152+
return action; // Returning action for testing purposes
153+
};
154+
155+
exec.displayName = 'logFileChanges.exec';
156+
exports.exec = exec;

test/CheckSensitive.test.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const path = require('path');
2+
const { exec } = require('../src/proxy/processors/push-action/checkSensitiveData.js'); // Adjust path as necessary
3+
const sinon = require('sinon');
4+
5+
describe('Sensitive Data Detection', () => {
6+
let logStub;
7+
8+
beforeEach(() => {
9+
logStub = sinon.stub(console, 'log'); // Stub console.log before each test
10+
});
11+
12+
afterEach(() => {
13+
logStub.restore(); // Restore console.log after each test
14+
});
15+
16+
it('should detect sensitive data in CSV file and block execution', async () => {
17+
const action = {
18+
steps: [{
19+
stepName: 'diff',
20+
content: {
21+
filePaths: [path.join(__dirname, 'test_data/sensitive_data.csv')] // Ensure this path is correct
22+
}
23+
}]
24+
};
25+
26+
await exec(null, action);
27+
28+
const loggedMessages = logStub.getCalls().map(call => call.args[0]);
29+
console.log('Captured log messages for CSV:', loggedMessages);
30+
31+
sinon.assert.calledWith(logStub, sinon.match(/Your push has been blocked due to sensitive data detection/));
32+
});
33+
34+
it('should detect sensitive data in XLSX file and block execution', async () => {
35+
const action = {
36+
steps: [{
37+
stepName: 'diff',
38+
content: {
39+
filePaths: [path.join(__dirname, 'test_data/sensitive_data2.xlsx')] // Ensure this path is correct
40+
}
41+
}]
42+
};
43+
44+
await exec(null, action);
45+
46+
const loggedMessages = logStub.getCalls().map(call => call.args[0]);
47+
console.log('Captured log messages for XLSX:', loggedMessages);
48+
49+
sinon.assert.calledWith(logStub, sinon.match(/Your push has been blocked due to sensitive data detection/));
50+
});
51+
52+
it('should detect sensitive data in a log file and block execution', async () => {
53+
const action = {
54+
steps: [{
55+
stepName: 'diff',
56+
content: {
57+
filePaths: [path.join(__dirname, 'test_data/sensitive_data3.log')] // Ensure this path is correct
58+
}
59+
}]
60+
};
61+
62+
await exec(null, action);
63+
64+
const loggedMessages = logStub.getCalls().map(call => call.args[0]);
65+
console.log('Captured log messages for log file:', loggedMessages);
66+
67+
sinon.assert.calledWith(logStub, sinon.match(/Your push has been blocked due to sensitive data detection/));
68+
});
69+
70+
it('should detect sensitive data in a JSON file and block execution', async () => {
71+
const action = {
72+
steps: [{
73+
stepName: 'diff',
74+
content: {
75+
filePaths: [path.join(__dirname, 'test_data/sensitive_data4.json')] // Ensure this path is correct
76+
}
77+
}]
78+
};
79+
80+
await exec(null, action);
81+
82+
const loggedMessages = logStub.getCalls().map(call => call.args[0]);
83+
console.log('Captured log messages for JSON file:', loggedMessages);
84+
85+
sinon.assert.calledWith(logStub, sinon.match(/Your push has been blocked due to sensitive data detection/));
86+
});
87+
88+
89+
});

test/CreateExcel.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const XLSX = require('xlsx');
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
// Example data with sensitive information
6+
const data = [
7+
{ Name: "John Doe", SSN: "123-45-6789", Email: "[email protected]" },
8+
{ Name: "Jane Smith", SSN: "987-65-4321", Email: "[email protected]" }
9+
];
10+
11+
const worksheet = XLSX.utils.json_to_sheet(data);
12+
const workbook = XLSX.utils.book_new();
13+
XLSX.utils.book_append_sheet(workbook, worksheet, "SensitiveData");
14+
15+
// Create the path to the test_data directory
16+
const testDataPath = path.join(__dirname, 'test_data'); // Ensure this points to the correct directory
17+
18+
// Create the test_data directory if it doesn't exist
19+
if (!fs.existsSync(testDataPath)){
20+
fs.mkdirSync(testDataPath, { recursive: true }); // Using recursive to ensure all directories are created
21+
}
22+
23+
// Write the Excel file to the test_data directory
24+
XLSX.writeFile(workbook, path.join(testDataPath, 'sensitive_data2.xlsx'));

test/test_data/sensitive_data.csv

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Name,SSN,Email
2+
John Doe,123-45-6789,[email protected]
3+
Jane Smith,987-65-4321,[email protected]
15.8 KB
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"username": "johndoe",
3+
"ssn": "123-45-6789"
4+
}

0 commit comments

Comments
 (0)