Skip to content

Commit f44852c

Browse files
committed
fix: fixed parsing of negative ignores and added tests
1 parent d2b26e4 commit f44852c

File tree

11 files changed

+154
-51
lines changed

11 files changed

+154
-51
lines changed

package.json

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,30 +45,32 @@
4545
"eslint-plugin-import": "^2.22.0",
4646
"eslint-plugin-prettier": "^3.1.4",
4747
"jest": "^26.4.2",
48+
"jsonschema": "^1.2.11",
4849
"prettier": "^2.1.1",
4950
"ts-jest": "^26.3.0",
5051
"typescript": "^4.0.2",
51-
"yalc": "^1.0.0-pre.44",
52-
"jsonschema": "^1.2.11",
53-
"write": "^2.0.0"
52+
"write": "^2.0.0",
53+
"yalc": "^1.0.0-pre.44"
5454
},
5555
"dependencies": {
56-
"@types/lodash.omit": "^4.5.6",
56+
"@deepcode/dcignore": "^1.0.2",
57+
"@snyk/fast-glob": "^3.2.6-patch",
58+
"@types/flat-cache": "^2.0.0",
5759
"@types/lodash.chunk": "^4.2.6",
60+
"@types/lodash.difference": "^4.5.6",
61+
"@types/lodash.omit": "^4.5.6",
5862
"@types/lodash.union": "^4.6.6",
5963
"@types/micromatch": "^4.0.1",
60-
"@types/flat-cache": "^2.0.0",
6164
"@types/sarif": "^2.1.3",
62-
"@deepcode/dcignore": "^1.0.2",
65+
"@types/uuid": "^8.3.0",
6366
"axios": "^0.21.1",
64-
"lodash.omit": "^4.5.0",
67+
"ignore": "^5.1.8",
6568
"lodash.chunk": "^4.2.0",
69+
"lodash.difference": "^4.5.0",
70+
"lodash.omit": "^4.5.0",
6671
"lodash.union": "^4.6.0",
67-
"ignore": "^5.1.8",
68-
"queue": "^6.0.1",
69-
"@snyk/fast-glob": "^3.2.6-patch",
7072
"micromatch": "^4.0.2",
71-
"@types/uuid": "^8.3.0",
73+
"queue": "^6.0.1",
7274
"uuid": "^8.3.2"
7375
}
7476
}

src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const EXCLUDED_NAMES = [GIT_FILENAME, GITIGNORE_FILENAME, DCIGNORE_FILENA
1212
export const CACHE_KEY = '.dccache';
1313
export const MAX_UPLOAD_ATTEMPTS = 5;
1414

15-
export const IGNORES_DEFAULT = [`**/${GIT_FILENAME}`];
15+
export const IGNORES_DEFAULT = [`**/${GIT_FILENAME}/**`];
1616

1717
export const IGNORE_FILES_NAMES = [GITIGNORE_FILENAME, DCIGNORE_FILENAME];
1818

src/files.ts

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import fg from '@snyk/fast-glob';
44
import micromatch from 'micromatch';
55
import crypto from 'crypto';
66
import union from 'lodash.union';
7+
import difference from 'lodash.difference';
78
import util from 'util';
89
import { Cache } from './cache';
910
import { HASH_ALGORITHM, ENCODE_TYPE, MAX_PAYLOAD, IGNORES_DEFAULT, IGNORE_FILES_NAMES, CACHE_KEY } from './constants';
@@ -76,11 +77,14 @@ export function parseFileIgnores(path: string): string[] {
7677
}
7778

7879
return rules.map(rule => {
79-
if (rule.startsWith('/') || rule.startsWith('**')) {
80-
return nodePath.posix.join(dirname, rule);
80+
let prefix = "";
81+
if (rule.startsWith('!')) {
82+
rule = rule.substring(1);
83+
prefix = "!";
8184
}
82-
83-
return nodePath.posix.join(dirname, '**', rule);
85+
return rule.startsWith('/') || rule.startsWith('**')
86+
? prefix + nodePath.posix.join(dirname, rule, '**')
87+
: prefix + nodePath.posix.join(dirname, '**', rule, '**');
8488
});
8589
}
8690

@@ -107,7 +111,6 @@ export async function collectIgnoreRules(
107111
IGNORE_FILES_NAMES.map(i => `*${i}`),
108112
{
109113
...fgOptions,
110-
ignore: fileIgnores,
111114
cwd: folder,
112115
followSymbolicLinks: symlinksEnabled,
113116
},
@@ -132,25 +135,22 @@ export function determineBaseDir(paths: string[]): string {
132135
return '';
133136
}
134137

135-
function searchFiles(
138+
async function* searchFiles(
136139
patterns: string[],
137140
cwd: string,
138141
symlinksEnabled: boolean,
139142
ignores: string[],
140-
): NodeJS.ReadableStream {
141-
const relIgnores = ignores.map(i => {
142-
if (i.startsWith(cwd)) {
143-
return i.slice(cwd.length + 1);
144-
}
145-
return i;
146-
});
147-
148-
return fg.stream(patterns, {
143+
): AsyncGenerator<string | Buffer> {
144+
const searcher = fg.stream(patterns, {
149145
...fgOptions,
150146
cwd,
151-
ignore: relIgnores,
152147
followSymbolicLinks: symlinksEnabled,
153148
});
149+
for await (const filePath of searcher) {
150+
if (filterIgnoredFiles([filePath.toString()], ignores).length) {
151+
yield filePath;
152+
}
153+
}
154154
}
155155

156156
/**
@@ -224,19 +224,14 @@ export async function prepareExtendingBundle(
224224
// Filter for supported extensions/files only
225225
let processingFiles: string[] = filterSupportedFiles(files, supportedFiles);
226226

227-
// Exclude files to be ignored based on ignore rules. We assume here, that ignore rules have not been changed
228-
processingFiles = micromatch(
229-
processingFiles,
230-
fileIgnores.map(p => `!${p}`),
231-
microMatchOptions,
232-
);
227+
// Exclude files to be ignored based on ignore rules. We assume here, that ignore rules have not been changed.
228+
processingFiles = filterIgnoredFiles(processingFiles, fileIgnores);
233229

234230
if (processingFiles.length) {
235231
// Determine existing files (minus removed)
236232
const entries = await fg(processingFiles, {
237233
...fgOptions,
238234
cwd: baseDir,
239-
ignore: fileIgnores,
240235
followSymbolicLinks: symlinksEnabled,
241236
objectMode: true,
242237
stats: true,
@@ -391,3 +386,11 @@ export function* composeFilePayloads(files: IFileInfo[], bucketSize = MAX_PAYLOA
391386
yield bucket.files;
392387
}
393388
}
389+
390+
export function filterIgnoredFiles(
391+
filePaths: string[],
392+
ignores: string[],
393+
) {
394+
const ignored = micromatch(filePaths, ignores, {...microMatchOptions, basename: false});
395+
return difference(filePaths, ignored);
396+
}

tests/analysis.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ describe('Functional test of analysis', () => {
6464
expect(bundle).toHaveProperty('sessionToken');
6565
expect(bundle).toHaveProperty('supportedFiles');
6666
expect(bundle).toHaveProperty('analysisURL');
67-
expect(Object.keys(bundle.analysisResults.files).length).toEqual(3);
67+
expect(Object.keys(bundle.analysisResults.files).length).toEqual(4);
6868
expect(
6969
bundle.analysisResults.files.hasOwnProperty(`${sampleProjectPath}/GitHubAccessTokenScrambler12.java`),
7070
).toBeTruthy();
@@ -75,7 +75,7 @@ describe('Functional test of analysis', () => {
7575
expect(new Set(bundle.analysisResults.coverage)).toEqual(
7676
new Set([
7777
{
78-
files: 1,
78+
files: 2,
7979
isSupported: true,
8080
lang: 'Java',
8181
},
@@ -89,7 +89,7 @@ describe('Functional test of analysis', () => {
8989

9090
// Check if emitter event happened
9191
expect(onSupportedFilesLoaded).toHaveBeenCalledTimes(2);
92-
expect(onScanFilesProgress).toHaveBeenCalledTimes(6);
92+
expect(onScanFilesProgress).toHaveBeenCalledTimes(7);
9393
expect(onCreateBundleProgress).toHaveBeenCalledTimes(3);
9494
expect(onAnalyseProgress).toHaveBeenCalled();
9595
expect(onAPIRequestLog).toHaveBeenCalled();

tests/api.spec.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { baseURL, authHost, sessionToken, TEST_TIMEOUT } from './constants/base';
2-
import { bundleFiles, bundleFilesFull } from './constants/sample';
2+
import { bundleFiles, bundleFilesFull, bundleFilePaths } from './constants/sample';
33
import { fromEntries } from '../src/lib/utils';
44
import {
55
getFilters,
@@ -16,9 +16,9 @@ import {
1616
reportEvent,
1717
} from '../src/http';
1818

19-
const fakeBundleId = '79925ce5d4dbfcb9f7f90f671bfcbdaebf394b3c91b49eb4d2b57f109d2abcc6';
19+
const fakeBundleId = '769e1811db5e98abdb65df9160228ad51646f535af28b80a8913813aec1bd331';
2020
let fakeBundleIdFull = '';
21-
const realBundleId = '39bc8dbc1e4fd197323fe352231ff3afad2cd2ea191b4abeb3613340c8752ea0';
21+
const realBundleId = 'ffdd7b1cb8e28d3859e6b7179bf4b871d1abd8a7a2cacf0002d5174aca3b6841';
2222
let realBundleIdFull = '';
2323

2424
const reportTelemetryRequest = {
@@ -112,6 +112,7 @@ describe('Requests to public API', () => {
112112
`/app.js`,
113113
`/db.js`,
114114
`/main.js`,
115+
`/not/ignored/this_should_not_be_ignored.java`,
115116
`/routes/index.js`,
116117
`/routes/sharks.js`,
117118
]);
@@ -135,6 +136,7 @@ describe('Requests to public API', () => {
135136
`/app.js`,
136137
`/db.js`,
137138
`/main.js`,
139+
`/not/ignored/this_should_not_be_ignored.java`,
138140
`/routes/index.js`,
139141
`/routes/sharks.js`,
140142
]);
@@ -199,6 +201,7 @@ describe('Requests to public API', () => {
199201
`/GitHubAccessTokenScrambler12.java`,
200202
`/db.js`,
201203
`/main.js`,
204+
`/not/ignored/this_should_not_be_ignored.java`,
202205
`/routes/index.js`,
203206
`/routes/sharks.js`,
204207
],
@@ -360,7 +363,7 @@ describe('Requests to public API', () => {
360363
expect(suggestion.severity).toEqual(2);
361364

362365
expect(suggestion.tags).toEqual(['maintenance', 'express', 'server', 'helmet']);
363-
expect(Object.keys(response.value.analysisResults.files).length).toEqual(3);
366+
expect(Object.keys(response.value.analysisResults.files).length).toEqual(4);
364367
expect(response.value.analysisResults.timing.analysis).toBeGreaterThanOrEqual(
365368
response.value.analysisResults.timing.fetchingCode,
366369
);
@@ -370,7 +373,7 @@ describe('Requests to public API', () => {
370373
expect(new Set(response.value.analysisResults.coverage)).toEqual(
371374
new Set([
372375
{
373-
files: 1,
376+
files: 2,
374377
isSupported: true,
375378
lang: 'Java',
376379
},
@@ -401,7 +404,8 @@ describe('Requests to public API', () => {
401404
} while (response.value.status !== AnalysisStatus.done);
402405

403406
expect(Object.keys(response.value.analysisResults.suggestions).length).toEqual(4);
404-
expect(Object.keys(response.value.analysisResults.files)).toEqual(['/GitHubAccessTokenScrambler12.java']);
407+
expect(new Set(Object.keys(response.value.analysisResults.files)))
408+
.toEqual(new Set(['/GitHubAccessTokenScrambler12.java', '/not/ignored/this_should_not_be_ignored.java']));
405409

406410
// Get analysis results without linters but with severity 3
407411
do {
@@ -419,7 +423,8 @@ describe('Requests to public API', () => {
419423
} while (response.value.status !== AnalysisStatus.done);
420424

421425
expect(Object.keys(response.value.analysisResults.suggestions).length).toEqual(2);
422-
expect(Object.keys(response.value.analysisResults.files)).toEqual(['/GitHubAccessTokenScrambler12.java']);
426+
expect(new Set(Object.keys(response.value.analysisResults.files)))
427+
.toEqual(new Set(['/GitHubAccessTokenScrambler12.java', '/not/ignored/this_should_not_be_ignored.java']));
423428
},
424429
TEST_TIMEOUT,
425430
);

tests/constants/sample.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ import { getFileInfo, notEmpty } from '../../src/files';
55

66
export const sampleProjectPath = path.resolve(__dirname, '../sample-repo');
77
export const supportedFiles = {
8-
extensions: ['.js', '.cpp', '.java'],
8+
extensions: ['.js', '.jsx', '.cpp', '.java'],
99
configFiles: ['.eslintrc.json', '.dcignore'],
1010
};
1111

12-
export const bundleFileIgnores = ['**/.git', `${sampleProjectPath}/**/models`, `${sampleProjectPath}/**/controllers`];
12+
export const bundleFileIgnores = [
13+
'**/.git/**',
14+
`${sampleProjectPath}/**/models/**`,
15+
`${sampleProjectPath}/**/controllers/**`,
16+
`${sampleProjectPath}/**/ignored/**`,
17+
`!${sampleProjectPath}/**/not/ignored/**`,
18+
`${sampleProjectPath}/**/*.jsx/**`
19+
];
1320

1421
export const bundleFilePaths = [
1522
'/.eslintrc.json',
@@ -20,6 +27,7 @@ export const bundleFilePaths = [
2027
'main.js',
2128
'routes/index.js',
2229
'routes/sharks.js',
30+
'not/ignored/this_should_not_be_ignored.java',
2331
];
2432

2533
async function getBundleFiles(withContent: boolean) {

tests/files.spec.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import * as nodePath from 'path';
55
import {
66
collectIgnoreRules,
77
collectBundleFiles,
8+
prepareExtendingBundle,
89
composeFilePayloads,
910
parseFileIgnores,
1011
getFileInfo,
1112
} from '../src/files';
1213

13-
import { sampleProjectPath, supportedFiles, bundleFiles, bundleFilesFull, bundleFileIgnores } from './constants/sample';
14+
import { sampleProjectPath, supportedFiles, bundleFiles, bundleFilePaths, bundleFilesFull, bundleFileIgnores } from './constants/sample';
1415

1516
describe('files', () => {
1617

@@ -34,6 +35,21 @@ describe('files', () => {
3435
expect(testFile.size).toEqual(239);
3536
});
3637

38+
it('extend bundle files', async () => {
39+
const testNewFiles = [
40+
`${sampleProjectPath}/app.js`,
41+
`${sampleProjectPath}/not/ignored/this_should_not_be_ignored.java`
42+
];
43+
const testRemovedFiles = [
44+
`${sampleProjectPath}/removed_from_the_parent_bundle.java`,
45+
`${sampleProjectPath}/ignored/this_should_be_ignored.java`
46+
];
47+
const parentBundle = [...testNewFiles, ...testRemovedFiles]
48+
const { files, removedFiles } = await prepareExtendingBundle(sampleProjectPath, parentBundle, supportedFiles, bundleFileIgnores);
49+
expect(files).toEqual((await bundleFiles).filter(obj => testNewFiles.includes(obj.filePath)));
50+
expect(removedFiles).toEqual(['/removed_from_the_parent_bundle.java']);
51+
});
52+
3753
it('collect bundle files with small max payload', async () => {
3854
// Limit size and we get fewer files
3955
const collector = collectBundleFiles(
@@ -53,7 +69,7 @@ describe('files', () => {
5369
it('collect bundle files with multiple folders', async () => {
5470
// Limit size and we get fewer files
5571
const folders = [nodePath.join(sampleProjectPath, 'models'), nodePath.join(sampleProjectPath, 'controllers')];
56-
const collector = collectBundleFiles(sampleProjectPath, folders, supportedFiles, bundleFileIgnores);
72+
const collector = collectBundleFiles(sampleProjectPath, folders, supportedFiles);
5773
const smallFiles = [];
5874
for await (const f of collector) {
5975
smallFiles.push(f);
@@ -68,7 +84,7 @@ describe('files', () => {
6884
it('compose file payloads', async () => {
6985
// Prepare all missing files first
7086
const payloads = [...composeFilePayloads(await bundleFilesFull, 1024)];
71-
expect(payloads.length).toEqual(4); // 4 chunks
87+
expect(payloads.length).toEqual(5); // 5 chunks
7288
expect(payloads[0].length).toEqual(4);
7389

7490
const testPayload = payloads[0][1];
@@ -81,7 +97,7 @@ describe('files', () => {
8197

8298
it('parse dc ignore file', () => {
8399
const patterns = parseFileIgnores(`${sampleProjectPath}/.dcignore`);
84-
expect(patterns.length).toEqual(2);
100+
expect(patterns.length).toEqual(5);
85101
});
86102

87103
it('support of utf-8 encoding', async () => {

tests/sample-repo/.dcignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
models
22
**/controllers
3+
4+
ignored
5+
!not/ignored
6+
*.jsx
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
3+
import org.apache.commons.codec.binary.Base64;
4+
5+
import javax.crypto.Cipher;
6+
import javax.crypto.spec.IvParameterSpec;
7+
import javax.crypto.spec.SecretKeySpec;
8+
9+
public class GitHubAccessTokenScrambler12 {
10+
static final String myInitVector = "RandomInitVector";
11+
static final String myKey = "GitHubErrorToken";
12+
13+
static String encrypt(String value) {
14+
try {
15+
IvParameterSpec iv = new IvParameterSpec(myInitVector.getBytes("UTF-8"));
16+
SecretKeySpec keySpec = new SecretKeySpec(myKey.getBytes("UTF-8"), "AES");
17+
18+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
19+
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
20+
21+
byte[] encrypted = cipher.doFinal(value.getBytes());
22+
return Base64.encodeBase64String(encrypted);
23+
} catch (Exception ex) {
24+
ex.printStackTrace();
25+
}
26+
return null;
27+
}
28+
}

0 commit comments

Comments
 (0)