Skip to content

Commit 518234f

Browse files
committed
feat: crypto check feature merged
2 parents 0be5039 + fee5329 commit 518234f

File tree

5 files changed

+375
-1
lines changed

5 files changed

+375
-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.checkForAiMlUsage,
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.checkSensitiveData = require('./checkSensitiveData').exec;
1616
exports.checkExifJpeg = require('./checkExifJpeg').exec;
1717
exports.checkForAiMlusage = require('./checkForAiMlUsage').exec;
18+
exports.checkCryptoImplementation = require('./checkCryptoImplementation').exec;

test/chain.test.js

Lines changed: 10 additions & 1 deletion
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(),
@@ -28,14 +29,15 @@ const mockPushProcessors = {
2829
getDiff: sinon.stub(),
2930
checkSensitiveData : sinon.stub(),
3031
clearBareClone: sinon.stub(),
31-
checkExifJpeg : sinon.stub(),
32+
checkEXIFJpeg : sinon.stub(),
3233
scanDiff: sinon.stub(),
3334
blockForAuth: sinon.stub(),
3435
};
3536
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';
@@ -113,6 +115,7 @@ describe('proxy chain', function () {
113115
mockPushProcessors.checkAuthorEmails.resolves(continuingAction);
114116
mockPushProcessors.checkUserPushPermission.resolves(continuingAction);
115117
mockPushProcessors.checkSensitiveData.resolves(continuingAction);
118+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
116119

117120
// this stops the chain from further execution
118121
mockPushProcessors.checkIfWaitingAuth.resolves({ type: 'push', continue: () => false, allowPush: false });
@@ -129,6 +132,7 @@ describe('proxy chain', function () {
129132
expect(mockPushProcessors.audit.called).to.be.true;
130133
expect(mockPushProcessors.checkSensitiveData.called).to.be.false;
131134
expect(mockPushProcessors.checkEXIFJpeg.called).to.be.false;
135+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
132136

133137
expect(result.type).to.equal('push');
134138
expect(result.allowPush).to.be.false;
@@ -140,12 +144,14 @@ describe('proxy chain', function () {
140144
const continuingAction = { type: 'push', continue: () => true, allowPush: false };
141145
mockPreProcessors.parseAction.resolves({ type: 'push' });
142146
mockPushProcessors.parsePush.resolves(continuingAction);
147+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
143148
mockPushProcessors.checkRepoInAuthorisedList.resolves(continuingAction);
144149
mockPushProcessors.checkCommitMessages.resolves(continuingAction);
145150
mockPushProcessors.checkAuthorEmails.resolves(continuingAction);
146151
mockPushProcessors.checkUserPushPermission.resolves(continuingAction);
147152
mockPushProcessors.checkSensitiveData.resolves(continuingAction);
148153
mockPushProcessors.checkEXIFJpeg.resolves(continuingAction);
154+
149155
// this stops the chain from further execution
150156

151157
mockPushProcessors.checkIfWaitingAuth.resolves({ type: 'push', continue: () => true, allowPush: true });
@@ -162,6 +168,7 @@ describe('proxy chain', function () {
162168
expect(mockPushProcessors.audit.called).to.be.true;
163169
expect(mockPushProcessors.checkSensitiveData.called).to.be.false;
164170
expect(mockPushProcessors.checkEXIFJpeg.called).to.be.false;
171+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
165172

166173
expect(result.type).to.equal('push');
167174
expect(result.allowPush).to.be.true;
@@ -187,6 +194,7 @@ describe('proxy chain', function () {
187194
mockPushProcessors.blockForAuth.resolves(continuingAction);
188195
mockPushProcessors.checkSensitiveData.resolves(continuingAction);
189196

197+
mockPushProcessors.checkCryptoImplementation.resolves(continuingAction);
190198

191199
const result = await chain.executeChain(req);
192200

@@ -206,6 +214,7 @@ describe('proxy chain', function () {
206214
expect(mockPushProcessors.blockForAuth.called).to.be.true;
207215
expect(mockPushProcessors.audit.called).to.be.true;
208216
expect(mockPushProcessors.checkSensitiveData.called).to.be.true;
217+
expect(mockPushProcessors.checkCryptoImplementation.called).to.be.true;
209218

210219
expect(result.type).to.equal('push');
211220
expect(result.allowPush).to.be.false;

0 commit comments

Comments
 (0)