Skip to content

Commit 1572931

Browse files
committed
feat: added feature to detect cryptimplementation
2 parents 1f311de + 57add83 commit 1572931

File tree

5 files changed

+373
-1
lines changed

5 files changed

+373
-1
lines changed

src/proxy/chain.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const pushActionChain = [
1414
proc.push.checkExifJpeg,
1515
proc.push.checkSensitiveData,
1616
proc.push.clearBareClone,
17+
proc.push.checkCryptoImplementation,
1718
proc.push.scanDiff,
1819
proc.push.blockForAuth,
1920
];
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
@@ -15,3 +15,4 @@ exports.clearBareClone = require('./clearBareClone').exec;
1515
exports.checkForAiMlUsage = require('./checkForAiMlUsage').exec;
1616
exports.checkExifJpeg = require('./checkExifJpeg').exec;
1717
exports.checkSensitiveData = require('./checkSensitiveData').exec;
18+
exports.checkCryptoImplementation = require('./checkCryptoImplementation').exec;

test/chain.test.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const mockPushProcessors = {
2121
audit: sinon.stub(),
2222
checkRepoInAuthorisedList: sinon.stub(),
2323
checkCommitMessages: sinon.stub(),
24+
checkCryptoImplementation: sinon.stub(),
2425
checkAuthorEmails: sinon.stub(),
2526
checkUserPushPermission: sinon.stub(),
2627
checkIfWaitingAuth: sinon.stub(),
@@ -37,6 +38,7 @@ mockPushProcessors.parsePush.displayName = 'parsePush';
3738
mockPushProcessors.audit.displayName = 'audit';
3839
mockPushProcessors.checkRepoInAuthorisedList.displayName = 'checkRepoInAuthorisedList';
3940
mockPushProcessors.checkCommitMessages.displayName = 'checkCommitMessages';
41+
mockPushProcessors.checkCryptoImplementation.displayName = 'checkCryptoImplementation';
4042
mockPushProcessors.checkAuthorEmails.displayName = 'checkAuthorEmails';
4143
mockPushProcessors.checkUserPushPermission.displayName = 'checkUserPushPermission';
4244
mockPushProcessors.checkIfWaitingAuth.displayName = 'checkIfWaitingAuth';
@@ -112,6 +114,7 @@ describe('proxy chain', function () {
112114
mockPushProcessors.checkAuthorEmails.resolves(continuingAction);
113115
mockPushProcessors.checkUserPushPermission.resolves(continuingAction);
114116
mockPushProcessors.checkSensitiveData.resolves(continuingAction);
117+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
115118

116119
// this stops the chain from further execution
117120
mockPushProcessors.checkIfWaitingAuth.resolves({ type: 'push', continue: () => false, allowPush: false });
@@ -126,6 +129,7 @@ describe('proxy chain', function () {
126129
expect(mockPushProcessors.checkIfWaitingAuth.called).to.be.true;
127130
expect(mockPushProcessors.pullRemote.called).to.be.false;
128131
expect(mockPushProcessors.audit.called).to.be.true;
132+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
129133

130134
expect(result.type).to.equal('push');
131135
expect(result.allowPush).to.be.false;
@@ -137,10 +141,12 @@ describe('proxy chain', function () {
137141
const continuingAction = { type: 'push', continue: () => true, allowPush: false };
138142
mockPreProcessors.parseAction.resolves({ type: 'push' });
139143
mockPushProcessors.parsePush.resolves(continuingAction);
144+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
140145
mockPushProcessors.checkRepoInAuthorisedList.resolves(continuingAction);
141146
mockPushProcessors.checkCommitMessages.resolves(continuingAction);
142147
mockPushProcessors.checkAuthorEmails.resolves(continuingAction);
143148
mockPushProcessors.checkUserPushPermission.resolves(continuingAction);
149+
144150
// this stops the chain from further execution
145151

146152
mockPushProcessors.checkIfWaitingAuth.resolves({ type: 'push', continue: () => true, allowPush: true });
@@ -155,6 +161,7 @@ describe('proxy chain', function () {
155161
expect(mockPushProcessors.checkIfWaitingAuth.called).to.be.true;
156162
expect(mockPushProcessors.pullRemote.called).to.be.false;
157163
expect(mockPushProcessors.audit.called).to.be.true;
164+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
158165

159166
expect(result.type).to.equal('push');
160167
expect(result.allowPush).to.be.true;
@@ -178,6 +185,7 @@ describe('proxy chain', function () {
178185
mockPushProcessors.clearBareClone.resolves(continuingAction);
179186
mockPushProcessors.scanDiff.resolves(continuingAction);
180187
mockPushProcessors.blockForAuth.resolves(continuingAction);
188+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
181189

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

@@ -196,7 +204,6 @@ describe('proxy chain', function () {
196204
expect(mockPushProcessors.scanDiff.called).to.be.true;
197205
expect(mockPushProcessors.blockForAuth.called).to.be.true;
198206
expect(mockPushProcessors.audit.called).to.be.true;
199-
expect(mockPushProcessors.checkSensitiveData.called).to.be.true;
200207

201208
expect(result.type).to.equal('push');
202209
expect(result.allowPush).to.be.false;

0 commit comments

Comments
 (0)