Skip to content

Commit f4a64e0

Browse files
committed
feat: merged feat for cryptography detection
2 parents e2702c9 + 7cf24ee commit f4a64e0

File tree

5 files changed

+374
-0
lines changed

5 files changed

+374
-0
lines changed

src/proxy/chain.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const pushActionChain = [
1515
proc.push.checkForAiMlUsage,
1616
proc.push.checkExifJpeg,
1717
proc.push.clearBareClone,
18+
proc.push.checkCryptoImplementation,
1819
proc.push.scanDiff,
1920
proc.push.blockForAuth,
2021
];
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
const Step = require('../../actions').Step;
2+
3+
// Common encryption-related patterns and keywords
4+
const CRYPTO_PATTERNS = {
5+
// Known non-standard encryption algorithms
6+
nonStandardAlgorithms: [
7+
'xor\\s*\\(',
8+
'rot13',
9+
'caesar\\s*cipher',
10+
'custom\\s*encrypt',
11+
'simple\\s*encrypt',
12+
'homebrew\\s*crypto',
13+
'custom\\s*hash'
14+
],
15+
16+
// Suspicious operations that might indicate custom crypto Implementation
17+
suspiciousOperations: [
18+
'bit\\s*shift',
19+
'bit\\s*rotate',
20+
'\\^=',
21+
'\\^',
22+
'>>>',
23+
'<<<',
24+
'shuffle\\s*bytes'
25+
],
26+
27+
// Common encryption-related variable names
28+
suspiciousVariables: [
29+
'cipher',
30+
'encrypt',
31+
'decrypt',
32+
'scramble',
33+
'salt(?!\\w)',
34+
'iv(?!\\w)',
35+
'nonce'
36+
]
37+
};
38+
39+
function analyzeCodeForCrypto(diffContent) {
40+
const issues = [];
41+
// Check for above mentioned cryto Patterns
42+
if(!diffContent) return issues;
43+
44+
CRYPTO_PATTERNS.nonStandardAlgorithms.forEach(pattern => {
45+
const regex = new RegExp(pattern, 'gi');
46+
const matches = diffContent.match(regex);
47+
if (matches) {
48+
issues.push({
49+
type: 'non_standard_algorithm',
50+
pattern: pattern,
51+
matches: matches,
52+
severity: 'high',
53+
message: `Detected possible non-standard encryption algorithm: ${matches.join(', ')}`
54+
});
55+
}
56+
});
57+
58+
CRYPTO_PATTERNS.suspiciousOperations.forEach(pattern => {
59+
const regex = new RegExp(pattern, 'gi');
60+
const matches = diffContent.match(regex);
61+
if (matches) {
62+
issues.push({
63+
type: 'suspicious_operation',
64+
pattern: pattern,
65+
matches: matches,
66+
severity: 'medium',
67+
message: `Detected suspicious cryptographic operation: ${matches.join(', ')}`
68+
});
69+
}
70+
});
71+
72+
CRYPTO_PATTERNS.suspiciousVariables.forEach(pattern => {
73+
const regex = new RegExp(pattern, 'gi');
74+
const matches = diffContent.match(regex);
75+
if (matches) {
76+
issues.push({
77+
type: 'suspicious_variable',
78+
pattern: pattern,
79+
matches: matches,
80+
severity: 'low',
81+
message: `Detected potential encryption-related variable: ${matches.join(', ')}`
82+
});
83+
}
84+
});
85+
86+
return issues;
87+
}
88+
89+
const exec = async (req, action) => {
90+
const step = new Step('checkCryptoImplementation');
91+
92+
try {
93+
let hasIssues = false;
94+
const allIssues = [];
95+
96+
for (const commit of action.commitData) {
97+
const diff = commit.diff || '';
98+
const issues = analyzeCodeForCrypto(diff);
99+
100+
if (issues.length > 0) {
101+
hasIssues = true;
102+
allIssues.push({
103+
commit: commit.hash,
104+
issues: issues
105+
});
106+
}
107+
}
108+
109+
if (hasIssues) {
110+
step.error = true;
111+
112+
const errorMessage = allIssues.map(commitIssues => {
113+
return `Commit ${commitIssues.commit}:\n` +
114+
commitIssues.issues.map(issue =>
115+
`- ${issue.severity.toUpperCase()}: ${issue.message}`
116+
).join('\n');
117+
}).join('\n\n');
118+
119+
step.setError(
120+
'\n\nYour push has been blocked.\n' +
121+
'Potential non-standard cryptographic implementations detected:\n\n' +
122+
`${errorMessage}\n\n` +
123+
'Please use standard cryptographic libraries instead of custom implementations.\n' +
124+
'Recommended: Use established libraries like crypto, node-forge, or Web Crypto API.\n'
125+
);
126+
}
127+
128+
action.addStep(step);
129+
return action;
130+
} catch (error) {
131+
step.error = true;
132+
step.setError(`Error analyzing crypto implementation: ${error.message}`);
133+
action.addStep(step);
134+
return action;
135+
}
136+
};
137+
138+
// exec.displayName = 'checkCryptoImplementation.exec';
139+
exports.exec = exec;
140+
exports.analyzeCodeForCrypto = analyzeCodeForCrypto;

src/proxy/processors/push-action/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ console.log(__dirname);
1212
exports.checkAuthorEmails = require('./checkAuthorEmails').exec;
1313
exports.checkUserPushPermission = require('./checkUserPushPermission').exec;
1414
exports.clearBareClone = require('./clearBareClone').exec;
15+
exports.checkCryptoImplementation = require('./checkCryptoImplementation').exec;
1516
exports.checkSensitiveData = require('./checkSensitiveData').exec;
1617
exports.checkForAiMlusage = require('./checkForAiMlUsage').exec;
1718
exports.checkExifJpeg = require('./checkExifJpeg').exec;

test/chain.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const mockPushProcessors = {
2020
audit: sinon.stub(),
2121
checkRepoInAuthorisedList: sinon.stub(),
2222
checkCommitMessages: sinon.stub(),
23+
checkCryptoImplementation: sinon.stub(),
2324
checkAuthorEmails: sinon.stub(),
2425
checkUserPushPermission: sinon.stub(),
2526
checkIfWaitingAuth: sinon.stub(),
@@ -36,6 +37,7 @@ mockPushProcessors.parsePush.displayName = 'parsePush';
3637
mockPushProcessors.audit.displayName = 'audit';
3738
mockPushProcessors.checkRepoInAuthorisedList.displayName = 'checkRepoInAuthorisedList';
3839
mockPushProcessors.checkCommitMessages.displayName = 'checkCommitMessages';
40+
mockPushProcessors.checkCryptoImplementation.displayName = 'checkCryptoImplementation';
3941
mockPushProcessors.checkAuthorEmails.displayName = 'checkAuthorEmails';
4042
mockPushProcessors.checkUserPushPermission.displayName = 'checkUserPushPermission';
4143
mockPushProcessors.checkIfWaitingAuth.displayName = 'checkIfWaitingAuth';
@@ -110,6 +112,7 @@ describe('proxy chain', function () {
110112
mockPushProcessors.checkEXIFJpeg.resolves(continuingAction);
111113
mockPushProcessors.checkAuthorEmails.resolves(continuingAction);
112114
mockPushProcessors.checkUserPushPermission.resolves(continuingAction);
115+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
113116
mockPushProcessors.checkSensitiveData.resolves(continuingAction);
114117

115118
// this stops the chain from further execution
@@ -125,6 +128,7 @@ describe('proxy chain', function () {
125128
expect(mockPushProcessors.checkIfWaitingAuth.called).to.be.true;
126129
expect(mockPushProcessors.pullRemote.called).to.be.false;
127130
expect(mockPushProcessors.audit.called).to.be.true;
131+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
128132

129133
expect(result.type).to.equal('push');
130134
expect(result.allowPush).to.be.false;
@@ -136,10 +140,12 @@ describe('proxy chain', function () {
136140
const continuingAction = { type: 'push', continue: () => true, allowPush: false };
137141
mockPreProcessors.parseAction.resolves({ type: 'push' });
138142
mockPushProcessors.parsePush.resolves(continuingAction);
143+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
139144
mockPushProcessors.checkRepoInAuthorisedList.resolves(continuingAction);
140145
mockPushProcessors.checkCommitMessages.resolves(continuingAction);
141146
mockPushProcessors.checkAuthorEmails.resolves(continuingAction);
142147
mockPushProcessors.checkUserPushPermission.resolves(continuingAction);
148+
143149
// this stops the chain from further execution
144150

145151
mockPushProcessors.checkIfWaitingAuth.resolves({ type: 'push', continue: () => true, allowPush: true });
@@ -154,6 +160,7 @@ describe('proxy chain', function () {
154160
expect(mockPushProcessors.checkIfWaitingAuth.called).to.be.true;
155161
expect(mockPushProcessors.pullRemote.called).to.be.false;
156162
expect(mockPushProcessors.audit.called).to.be.true;
163+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
157164

158165
expect(result.type).to.equal('push');
159166
expect(result.allowPush).to.be.true;
@@ -177,6 +184,7 @@ describe('proxy chain', function () {
177184
mockPushProcessors.clearBareClone.resolves(continuingAction);
178185
mockPushProcessors.scanDiff.resolves(continuingAction);
179186
mockPushProcessors.blockForAuth.resolves(continuingAction);
187+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
180188

181189
const result = await chain.executeChain(req);
182190

@@ -195,6 +203,7 @@ describe('proxy chain', function () {
195203
expect(mockPushProcessors.scanDiff.called).to.be.true;
196204
expect(mockPushProcessors.blockForAuth.called).to.be.true;
197205
expect(mockPushProcessors.audit.called).to.be.true;
206+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
198207
expect(mockPushProcessors.checkSensitiveData.called).to.be.true;
199208

200209
expect(result.type).to.equal('push');

0 commit comments

Comments
 (0)