@netlify/build 34.0.0 → 34.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,6 @@ const coreStep = async function ({ buildDir, logs, netlifyConfig, explicitSecret
12
12
  // When the flag is disabled, we may still run the scan if a secrets scan would otherwise take place anyway
13
13
  // In this case, we hide any output to the user and simply gather the information in our logs
14
14
  const enhancedScanShouldRunInActiveMode = featureFlags?.enhanced_secret_scan_impacts_builds ?? false;
15
- const useMinimalChunks = featureFlags?.secret_scanning_minimal_chunks;
16
15
  systemLog?.({ passedSecretKeys, buildDir });
17
16
  if (!isSecretsScanningEnabled(envVars)) {
18
17
  logSecretsScanSkipMessage(logs, 'Secrets scanning disabled via SECRETS_SCAN_ENABLED flag set to false.');
@@ -63,7 +62,6 @@ const coreStep = async function ({ buildDir, logs, netlifyConfig, explicitSecret
63
62
  filePaths,
64
63
  enhancedScanning: enhancedScanShouldRun,
65
64
  omitValuesFromEnhancedScan: getOmitValuesFromEnhancedScanForEnhancedScanFromEnv(envVars),
66
- useMinimalChunks,
67
65
  });
68
66
  secretMatches = scanResults.matches.filter((match) => !match.enhancedMatch);
69
67
  enhancedSecretMatches = scanResults.matches.filter((match) => match.enhancedMatch);
@@ -9,7 +9,6 @@ interface ScanArgs {
9
9
  filePaths: string[];
10
10
  enhancedScanning?: boolean;
11
11
  omitValuesFromEnhancedScan?: unknown[];
12
- useMinimalChunks: boolean;
13
12
  }
14
13
  interface MatchResult {
15
14
  lineNumber: number;
@@ -98,7 +97,7 @@ export declare function getFilePathsToScan({ env, base }: {
98
97
  * @param scanArgs {ScanArgs} scan options
99
98
  * @returns promise with all of the scan results, if any
100
99
  */
101
- export declare function scanFilesForKeyValues({ env, keys, filePaths, base, enhancedScanning, omitValuesFromEnhancedScan, useMinimalChunks, }: ScanArgs): Promise<ScanResults>;
100
+ export declare function scanFilesForKeyValues({ env, keys, filePaths, base, enhancedScanning, omitValuesFromEnhancedScan, }: ScanArgs): Promise<ScanResults>;
102
101
  /**
103
102
  * ScanResults are all of the finds for all keys and their disparate locations. Scanning is
104
103
  * async in streams so order can change a lot. Some matches are the result of an env var explictly being marked as secret,
@@ -1,6 +1,5 @@
1
1
  import { createReadStream, promises as fs, existsSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { createInterface } from 'node:readline';
4
3
  import { fdir } from 'fdir';
5
4
  import { minimatch } from 'minimatch';
6
5
  import { LIKELY_SECRET_PREFIXES, SAFE_LISTED_VALUES } from './secret_prefixes.js';
@@ -221,7 +220,7 @@ const omitPathMatches = (relativePath, omitPaths) => {
221
220
  * @param scanArgs {ScanArgs} scan options
222
221
  * @returns promise with all of the scan results, if any
223
222
  */
224
- export async function scanFilesForKeyValues({ env, keys, filePaths, base, enhancedScanning, omitValuesFromEnhancedScan = [], useMinimalChunks = false, }) {
223
+ export async function scanFilesForKeyValues({ env, keys, filePaths, base, enhancedScanning, omitValuesFromEnhancedScan = [], }) {
225
224
  const scanResults = {
226
225
  matches: [],
227
226
  scannedFilesCount: 0,
@@ -242,14 +241,19 @@ export async function scanFilesForKeyValues({ env, keys, filePaths, base, enhanc
242
241
  }, {});
243
242
  scanResults.scannedFilesCount = filePaths.length;
244
243
  let settledPromises = [];
245
- const searchStream = useMinimalChunks ? searchStreamMinimalChunks : searchStreamReadline;
246
244
  // process the scanning in batches to not run into memory issues by
247
245
  // processing all files at the same time.
248
246
  while (filePaths.length > 0) {
249
247
  const chunkSize = 200;
250
248
  const batch = filePaths.splice(0, chunkSize);
251
249
  settledPromises = settledPromises.concat(await Promise.allSettled(batch.map((file) => {
252
- return searchStream({ basePath: base, file, keyValues, enhancedScanning, omitValuesFromEnhancedScan });
250
+ return searchStreamMinimalChunks({
251
+ basePath: base,
252
+ file,
253
+ keyValues,
254
+ enhancedScanning,
255
+ omitValuesFromEnhancedScan,
256
+ });
253
257
  })));
254
258
  }
255
259
  settledPromises.forEach((result) => {
@@ -259,125 +263,6 @@ export async function scanFilesForKeyValues({ env, keys, filePaths, base, enhanc
259
263
  });
260
264
  return scanResults;
261
265
  }
262
- /**
263
- * Search stream implementation using node:readline
264
- */
265
- const searchStreamReadline = ({ basePath, file, keyValues, enhancedScanning, omitValuesFromEnhancedScan = [], }) => {
266
- return new Promise((resolve, reject) => {
267
- const filePath = path.resolve(basePath, file);
268
- const inStream = createReadStream(filePath);
269
- const rl = createInterface({ input: inStream, terminal: false });
270
- const matches = [];
271
- const keyVals = [].concat(...Object.values(keyValues));
272
- function getKeyForValue(val) {
273
- let key = '';
274
- for (const [secretKeyName, valuePermutations] of Object.entries(keyValues)) {
275
- if (valuePermutations.includes(val)) {
276
- key = secretKeyName;
277
- }
278
- }
279
- return key;
280
- }
281
- // how many lines is the largest multiline string
282
- let maxMultiLineCount = 1;
283
- keyVals.forEach((valVariant) => {
284
- maxMultiLineCount = Math.max(maxMultiLineCount, valVariant.split('\n').length);
285
- });
286
- const lines = [];
287
- let lineNumber = 0;
288
- rl.on('line', function (line) {
289
- // iterating here so the first line will always appear as line 1 to be human friendly
290
- // and match what an IDE would show for a line number.
291
- lineNumber++;
292
- if (typeof line === 'string') {
293
- if (enhancedScanning) {
294
- matches.push(...findLikelySecrets({ text: line, omitValuesFromEnhancedScan }).map(({ prefix }) => ({
295
- key: prefix,
296
- file,
297
- lineNumber,
298
- enhancedMatch: true,
299
- })));
300
- }
301
- if (maxMultiLineCount > 1) {
302
- lines.push(line);
303
- }
304
- // only track the max number of lines needed to match our largest
305
- // multiline value. If we get above that remove the first value from the list
306
- if (lines.length > maxMultiLineCount) {
307
- lines.shift();
308
- }
309
- keyVals.forEach((valVariant) => {
310
- // matching of single/whole values
311
- if (line.includes(valVariant)) {
312
- matches.push({
313
- file,
314
- lineNumber,
315
- key: getKeyForValue(valVariant),
316
- enhancedMatch: false,
317
- });
318
- return;
319
- }
320
- // matching of multiline values
321
- if (isMultiLineVal(valVariant)) {
322
- // drop empty values at beginning and end
323
- const multiStringLines = valVariant.split('\n');
324
- // drop early if we don't have enough lines for all values
325
- if (lines.length < multiStringLines.length) {
326
- return;
327
- }
328
- let stillMatches = true;
329
- let fullMatch = false;
330
- multiStringLines.forEach((valLine, valIndex) => {
331
- if (valIndex === 0) {
332
- // first lines have to end with the line value
333
- if (!lines[valIndex].endsWith(valLine)) {
334
- stillMatches = false;
335
- }
336
- }
337
- else if (valIndex !== multiStringLines.length - 1) {
338
- // middle lines have to have full line match
339
- // middle lines
340
- if (lines[valIndex] !== valLine) {
341
- stillMatches = false;
342
- }
343
- }
344
- else {
345
- // last lines have start with the value
346
- if (!lines[valIndex].startsWith(valLine)) {
347
- stillMatches = false;
348
- }
349
- if (stillMatches === true) {
350
- fullMatch = true;
351
- }
352
- }
353
- });
354
- if (fullMatch) {
355
- matches.push({
356
- file,
357
- lineNumber: lineNumber - lines.length + 1,
358
- key: getKeyForValue(valVariant),
359
- enhancedMatch: false,
360
- });
361
- return;
362
- }
363
- }
364
- });
365
- }
366
- });
367
- rl.on('error', function (error) {
368
- if (error?.code === 'EISDIR') {
369
- // file path is a directory - do nothing
370
- resolve(matches);
371
- }
372
- else {
373
- reject(error);
374
- }
375
- });
376
- rl.on('close', function () {
377
- resolve(matches);
378
- });
379
- });
380
- };
381
266
  /**
382
267
  * Search stream implementation using just read stream that allows to buffer less content
383
268
  */
@@ -610,6 +495,3 @@ export function groupScanResultsByKeyAndScanType(scanResults) {
610
495
  sortMatches(enhancedSecretMatchesByKeys);
611
496
  return { secretMatches: secretMatchesByKeys, enhancedSecretMatches: enhancedSecretMatchesByKeys };
612
497
  }
613
- function isMultiLineVal(v) {
614
- return typeof v === 'string' && v.includes('\n');
615
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/build",
3
- "version": "34.0.0",
3
+ "version": "34.0.1",
4
4
  "description": "Netlify build module",
5
5
  "type": "module",
6
6
  "exports": "./lib/index.js",
@@ -67,16 +67,16 @@
67
67
  "license": "MIT",
68
68
  "dependencies": {
69
69
  "@bugsnag/js": "^8.0.0",
70
- "@netlify/blobs": "^10.0.3",
70
+ "@netlify/blobs": "^10.0.4",
71
71
  "@netlify/cache-utils": "^6.0.3",
72
72
  "@netlify/config": "^23.1.0",
73
- "@netlify/edge-bundler": "14.0.6",
74
- "@netlify/functions-utils": "^6.0.14",
73
+ "@netlify/edge-bundler": "14.0.7",
74
+ "@netlify/functions-utils": "^6.0.15",
75
75
  "@netlify/git-utils": "^6.0.2",
76
76
  "@netlify/opentelemetry-utils": "^2.0.1",
77
77
  "@netlify/plugins-list": "^6.80.0",
78
78
  "@netlify/run-utils": "^6.0.2",
79
- "@netlify/zip-it-and-ship-it": "13.0.0",
79
+ "@netlify/zip-it-and-ship-it": "13.0.1",
80
80
  "@sindresorhus/slugify": "^2.0.0",
81
81
  "ansi-escapes": "^7.0.0",
82
82
  "chalk": "^5.0.0",
@@ -156,5 +156,5 @@
156
156
  "engines": {
157
157
  "node": ">=18.14.0"
158
158
  },
159
- "gitHead": "cc8bccf13040e0e43282845c5a3a6d17db659ce8"
159
+ "gitHead": "e471abe7b07fb8a34110d06bd8857c89f1d84142"
160
160
  }