Close Open Privacy Scan

bolt Snapshot: commit 7a6615e
science engine v3
schedule 2026-07-07T15:05:49.144322+00:00

verified_user Possible application data leak

Potential data exfiltration identified in application code.

App Privacy Score

72 /100
Medium privacy risk — possible application leak

Medium risk · 14 finding(s)

Dependency score: 97 (Low risk)

bar_chart Score Breakdown

pii_flow −15
egress −10
env_fs −3

list Scan Summary

0 high 1 medium 13 low
First-party packages: 1
Dependency packages: 2
Ecosystem: npm

swap_horiz Potential data exfiltration in application code

External domains: api.opencollective.com

medium first-party (npm) Credentials parsed from the request URL are applied as authorization on the same outbound HTTP request. This is intentional URL authentication, not unexpected data exfiltration.
repo/lib/adapters/http.js:863 repo/lib/adapters/http.js:1064

</> First-Party Code

first-party (npm)

npm first-party
medium pii_flow production #8de42e291f4ce5a9 Credentials parsed from the request URL are applied as authorization on the same outbound HTTP request. This is intentional URL authentication, not unexpected data exfiltration.
repo/lib/adapters/http.js:1064 · flow /tmp/closeopen-m3kp7g7y/repo/lib/adapters/http.js:863 → /tmp/closeopen-m3kp7g7y/repo/lib/adapters/http.js:1064
      req = transport.request(options, function handleResponse(res) {
        clearConnectPhaseTimer();

        if (req.destroyed) return;

        const streams = [res];

        const responseLength = utils.toFiniteNumber(res.headers['content-length']);

        if (onDownloadProgress || maxDownloadRate) {
          const transformStream = new AxiosTransformStream({
            maxRate: utils.toFiniteNumber(maxDownloadRate),
          });

          onDownloadProgress &&
            transformStream.on(
              'progress',
              flushOnFinish(
                transformStream,
                progressEventDecorator(
                  responseLength,
                  progressEventReducer(asyncDecorator(onDownloadProgress, scheduleProgress), true, 3)
                )
              )
            );

          streams.push(transformStream);
        }

        // decompress the response body transparently if required
        let responseStream = res;

        // return the last request in case of redirects
        const lastRequest = res.req || req;

        // if decompress disabled we should not decompress
        if (decompress !== false && res.headers['content-encoding']) {
          // if no content, but headers still say that it is encoded,
          // remove the header not confuse downstream operations
          if (method === 'HEAD' || res.statusCode === 204) {
            delete res.headers['content-encoding'];
          }

          switch ((res.headers['content-encoding'] || '').toLowerCase()) {
            /*eslint default-case:0*/
            case 'gzip':
            case 'x-gzip':
            case 'compress':
            case 'x-compress':
              // add the unzipper to the body stream processing pipeline
              streams.push(zlib.createUnzip(zlibOptions));

              // remove the content-encoding in order to not confuse downstream operations
              delete res.headers['content-encoding'];
              break;
            case 'deflate':
              streams.push(new ZlibHeaderTransformStream());

              // add the unzipper to the body stream processing pipeline
              streams.push(zlib.createUnzip(zlibOptions));

              // remove the content-encoding in order to not confuse downstream operations
              delete res.headers['content-encoding'];
              break;
            case 'br':
              if (isBrotliSupported) {
                streams.push(zlib.createBrotliDecompress(brotliOptions));
                delete res.headers['content-encoding'];
              }
              break;
            case 'zstd':
              if (isZstdSupported) {
                streams.push(zlib.createZstdDecompress(zstdOptions));
                delete res.headers['content-encoding'];
              }
              break;
          }
        }

        responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];

        const response = {
          status: res.statusCode,
          statusText: res.statusMessage,
          headers: new AxiosHeaders(res.headers),
          config,
          request: lastRequest,
        };

        if (responseType === 'stream') {
          // Enforce maxContentLength on streamed responses; previously this
          // was applied only to buffered responses.
          if (maxContentLength > -1) {
            const limit = maxContentLength;
            const source = responseStream;
            async function* enforceMaxContentLength() {
              let totalResponseBytes = 0;
              for await (const chunk of source) {
                totalResponseBytes += chunk.length;
                if (totalResponseBytes > limit) {
                  throw new AxiosError(
                    'maxContentLength size of ' + limit + ' exceeded',
                    AxiosError.ERR_BAD_RESPONSE,
                    config,
                    lastRequest
                  );
                }
                yield chunk;
              }
            }
            responseStream = stream.Readable.from(enforceMaxContentLength(), {
              objectMode: false,
            });
          }
          response.data = responseStream;
          settle(resolve, reject, response);
        } else {
          const responseBuffer = [];
          let totalResponseBytes = 0;

          responseStream.on('data', function handleStreamData(chunk) {
            responseBuffer.push(chunk);
            totalResponseBytes += chunk.length;

            // make sure the content length is not over the maxContentLength if specified
            if (maxContentLength > -1 && totalResponseBytes > maxContentLength) {
              // stream.destroy() emit aborted event before calling reject() on Node.js v16
              rejected = true;
              responseStream.destroy();
              abort(
                new AxiosError(
                  'maxContentLength size of ' + maxContentLength + ' exceeded',
                  AxiosError.ERR_BAD_RESPONSE,
                  config,
                  lastRequest
                )
              );
            }
          });

          responseStream.on('aborted', function handlerStreamAborted() {
            if (rejected) {
              return;
            }

            const err = new AxiosError(
              'stream has been aborted',
              AxiosError.ERR_BAD_RESPONSE,
              config,
              lastRequest,
              response
            );
            responseStream.destroy(err);
            reject(err);
          });

          responseStream.on('error', function handleStreamError(err) {
            if (rejected) return;
            reject(AxiosError.from(err, null, config, lastRequest, response));
          });

          responseStream.on('end', function handleStreamEnd() {
            try {
              let responseData =
                responseBuffer.length === 1 ? responseBuffer[0] : Buffer.concat(responseBuffer);
              if (responseType !== 'arraybuffer') {
                responseData = responseData.toString(responseEncoding);
                if (!responseEncoding || responseEncoding === 'utf8') {
                  responseData = utils.stripBOM(responseData);
                }
              }
              response.data = responseData;
            } catch (err) {
              return reject(AxiosError.from(err, null, config, response.request, response));
            }
            settle(resolve, reject, response);
          });
        }

        abortEmitter.once('abort', (err) => {
          if (!responseStream.destroyed) {
            responseStream.emit('error', err);
            responseStream.destroy();
          }
        });
      });

User/PII-bearing data flows to an external sink — the classic data-exfiltration shape.

Fix: Confirm no user identifiers reach this sink; redact/hash before sending, or remove the flow.

expand_more 10 low-confidence finding(s)
low egress production #2f95b411a41d49c3 Hardcoded external endpoint. Review what data is sent to this destination.
repo/docs/scripts/process-sponsors.js:120
  const response = await axios.post('https://api.opencollective.com/graphql/v2', {
    query: getAllSponsorsQuery,
  });

Data is sent to a hardcoded external endpoint; review what leaves the process.

Fix: Verify the destination and that only non-sensitive data is sent; pin and audit the dependency.

low egress production #7c708fcb3f1140b2 Hardcoded external endpoint. Review what data is sent to this destination.
repo/docs/scripts/process-sponsors.js:134
  const response = await axios.post('https://api.opencollective.com/graphql/v2', {
    query: getActiveSponsorsQuery,
  });

Data is sent to a hardcoded external endpoint; review what leaves the process.

Fix: Verify the destination and that only non-sensitive data is sent; pin and audit the dependency.

low env_fs production #2a13497dbfdf31df Filesystem access.
repo/docs/scripts/process-sponsors.js:309
    fs.writeFileSync('./data/sponsors.json', JSON.stringify(sponsorsByTier, null, 2));

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #70eb86efb77e0024 Filesystem access.
repo/examples/server.js:1
import fs from 'fs';

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #328dd332c65cde39 Filesystem access.
repo/gulpfile.js:39
  const npm = JSON.parse(await fs.readFile('package.json'));

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #c7fe461f6d93c197 Filesystem access.
repo/gulpfile.js:51
    await fs.writeFile('package.json', JSON.stringify(npm, null, 2));

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #2ac3be92a0eb407d Filesystem access.
repo/gulpfile.js:61
  var npm = JSON.parse(await fs.readFile('package.json'));

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #5b16698b0fb4f3d0 Filesystem access.
repo/gulpfile.js:65
  await fs.writeFile(
    envFilePath,
    Object.entries({
      VERSION: (argv.bump || npm.version).replace(/^v/, ''),
    })
      .map(([key, value]) => {
        return `export const ${key} = ${JSON.stringify(value)};`;
      })
      .join('\n')
  );

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #726439f932e999f2 Environment-variable access.
repo/lib/helpers/shouldBypassProxy.js:283
  const noProxy = (process.env.no_proxy || process.env.NO_PROXY || '').toLowerCase();

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs production #4024ccde1ae3af0e Filesystem access.
repo/sandbox/server.js:1
import fs from 'fs';

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

</> Dependencies

form-data

npm dependency
expand_more 1 low-confidence finding(s)
low env_fs dependency Excluded from app score #9817c60463d87ee3 Filesystem access.
pkgs/npm/[email protected]/lib/form_data.js:9
var fs = require('fs');

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

proxy-from-env

npm dependency
expand_more 2 low-confidence finding(s)
low env_fs dependency Excluded from app score #c65618bbdcea4acc Environment-variable access.
pkgs/npm/[email protected]/index.cjs:102
  return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || '';

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

low env_fs dependency Excluded from app score #2dd84728d77fcd7c Environment-variable access.
pkgs/npm/[email protected]/index.js:102
  return process.env[key.toLowerCase()] || process.env[key.toUpperCase()] || '';

Reads environment variables or the filesystem — an inventory-level capability, not a leak on its own.

Fix: Usually benign; confirm any secret read here is not later sent externally.

Skipped dependencies

Production

  • https-proxy-agent prod — dist-only: no readable source