Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*
package-lock.json
/git-proxy-test


# Diagnostic reports (https://nodejs.org/api/report.html)
Expand Down
3 changes: 1 addition & 2 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"


npx --no -- commitlint --edit ${1} && npm run lint
111 changes: 110 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"concurrently": "^8.0.0",
"connect-mongo": "^5.1.0",
"cors": "^2.8.5",
"csv-parser": "^3.0.0",
"diff2html": "^3.4.33",
"express": "^4.18.2",
"express-http-proxy": "^2.0.0",
Expand All @@ -73,6 +74,7 @@
"react-html-parser": "^2.0.2",
"react-router-dom": "6.26.2",
"uuid": "^10.0.0",
"xlsx": "^0.18.5",
"yargs": "^17.7.2"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion src/proxy/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ const pushActionChain = [
proc.push.checkIfWaitingAuth,
proc.push.pullRemote,
proc.push.writePack,
proc.push.getDiff,
proc.push.getDiff,
proc.push.checkSensitiveData, // checkSensitiveData added
proc.push.clearBareClone,
proc.push.scanDiff,
proc.push.blockForAuth,
];


const pullActionChain = [proc.push.checkRepoInAuthorisedList];

let pluginsInserted = false;
Expand Down
136 changes: 136 additions & 0 deletions src/proxy/processors/push-action/checkSensitiveData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
const fs = require('fs');
const csv = require('csv-parser');
const XLSX = require('xlsx');
const path = require('path');
// const { exec: getDiffExec } = require('./getDiff');
// Function to check for sensitive data patterns
const checkForSensitiveData = (cell) => {
const sensitivePatterns = [
/\d{3}-\d{2}-\d{4}/, // Social Security Number (SSN)
/\b\d{16}\b/, // Credit card numbers
/\b\d{5}-\d{4}\b/, // ZIP+4 codes
// Add more patterns as needed
];
Comment on lines +12 to +17
Copy link
Contributor

@rgmz rgmz Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent behind this change is good, though it must be noted these will produce a large number of false positives.

Ideally this wouldn't block (only warn), or would have an easy way to exclude false positives.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point @rgmz ! I will think about this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Not to mention, this does not cover all geographies.

I'm inclined to merge it as it is not configured by default. A more holistic approach with better heuristics is worth investing in for the GitProxy project granted but this is a good enough start.

return sensitivePatterns.some(pattern => {
if (pattern.test(String(cell))) {
console.log(`\x1b[31mDetected sensitive data: ${cell}\x1b[0m`); // Log the detected sensitive data in red
return true;
}
return false;
});
};
// Function to process CSV files
const processCSV = async (filePath) => {
return new Promise((resolve, reject) => {
let sensitiveDataFound = false;
fs.createReadStream(filePath)
.pipe(csv())
.on('data', (row) => {
for (const [key, value] of Object.entries(row)) {
if (checkForSensitiveData(value)) {
console.log(`\x1b[33mSensitive data found in CSV: ${key}: ${value}\x1b[0m`); // Log in yellow
sensitiveDataFound = true;
}
}
})
.on('end', () => {
if (!sensitiveDataFound) {
console.log('No sensitive data found in CSV.');
}
resolve(sensitiveDataFound); // Resolve with the flag indicating if sensitive data was found
})
.on('error', (err) => {
console.error(`Error reading CSV file: ${err.message}`);
reject(err); // Reject the promise on error
});
});
};
// Function to process XLSX files
const processXLSX = async (filePath) => {
return new Promise((resolve, reject) => {
let sensitiveDataFound = false;
try {
const workbook = XLSX.readFile(filePath);
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
const jsonData = XLSX.utils.sheet_to_json(sheet);
jsonData.forEach((row) => {
for (const [key, value] of Object.entries(row)) {
if (checkForSensitiveData(value)) {
console.log(`\x1b[33mSensitive data found in XLSX: ${key}: ${value}\x1b[0m`); // Log in yellow
sensitiveDataFound = true;
}
}
});
if (!sensitiveDataFound) {
console.log('No sensitive data found in XLSX.');
}
resolve(sensitiveDataFound); // Resolve with the flag indicating if sensitive data was found
} catch (error) {
console.error(`Error reading XLSX file: ${error.message}`);
reject(error); // Reject the promise on error
}
});
};
// Function to check for sensitive data in .log and .json files
const checkLogJsonFiles = async (filePath) => {
return new Promise((resolve, reject) => {
let sensitiveDataFound = false;
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`Error reading file ${filePath}: ${err.message}`);
return reject(err);
}
if (checkForSensitiveData(data)) {
console.log(`\x1b[33mSensitive data found in ${filePath}\x1b[0m`);
sensitiveDataFound = true;
}
resolve(sensitiveDataFound);
});
});
};
// Function to parse the file based on its extension
const parseFile = async (filePath) => {
const ext = path.extname(filePath).toLowerCase();

switch (ext) {
case '.csv':
return await processCSV(filePath);
case '.xlsx':
return await processXLSX(filePath);
case '.log':
return await checkLogJsonFiles(filePath);
case '.json':
return await checkLogJsonFiles(filePath);
default:
// Skip unsupported file types without logging
return false; // Indicate that no sensitive data was found for unsupported types
}
};
// Async exec function to handle actions
const exec = async (req, action) => {
// getDiffExec(req, action); // Call to getDiffExec if necessary
const diffStep = action.steps.find((s) => s.stepName === 'diff');
if (diffStep && diffStep.content) {
console.log('Diff content:', diffStep.content);
const filePaths = diffStep.content.filePaths || [];
if (filePaths.length > 0) {
// Check for sensitive data in all files
const sensitiveDataFound = await Promise.all(filePaths.map(parseFile));
const anySensitiveDataDetected = sensitiveDataFound.some(found => found); // Check if any file reported sensitive data
if (anySensitiveDataDetected) {
action.pushBlocked = true; // Block the push
action.error = true; // Set error flag
action.errorMessage = 'Your push has been blocked due to sensitive data detection.'; // Set error message
console.log(action.errorMessage);
}
} else {
console.log('No file paths provided in the diff step.');
}
} else {
console.log('No diff content available.');
}
return action; // Returning action for testing purposes
};
exec.displayName = 'logFileChanges.exec';
exports.exec = exec;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
exports.exec = exec;
exports.exec = exec;

1 change: 1 addition & 0 deletions src/proxy/processors/push-action/getDiff.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const child = require('child_process');
const Step = require('../../actions').Step;


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mistaken change?

Suggested change

const exec = async (req, action) => {
const step = new Step('diff');

Expand Down
1 change: 1 addition & 0 deletions src/proxy/processors/push-action/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ exports.checkCommitMessages = require('./checkCommitMessages').exec;
exports.checkAuthorEmails = require('./checkAuthorEmails').exec;
exports.checkUserPushPermission = require('./checkUserPushPermission').exec;
exports.clearBareClone = require('./clearBareClone').exec;
exports.checkSensitiveData = require('./checkSensitiveData').exec;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add missing newline at the end of the file.

Suggested change
exports.checkSensitiveData = require('./checkSensitiveData').exec;
exports.checkSensitiveData = require('./checkSensitiveData').exec;

Loading
Loading