From 9b38e9c1faa26e23e6787b62d18998fdd33becb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Thu, 30 Jul 2026 10:12:45 +0200 Subject: [PATCH 1/2] chore(deps): bump brace-expansion in website lockfile to resolve security alert (#3933) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Lockfile-only bump of `brace-expansion` to its patched versions in `website/yarn.lock` (high — ReDoS), within existing ranges. The root `yarn.lock` already carries the patched `^`-range copies (brace-expansion 5.0.8, axios 1.18.1); `docs/yarn.lock` and `website/versioned_docs/version-3.17/yarn.lock` only contain the already-patched 1.x line (1.1.16). ### Not addressed here (need a decision on `resolutions`) The remaining root alerts can't close via a lockfile bump because dev tooling pins vulnerable versions: - `axios` 1.16.0 and `brace-expansion` 5.0.6 — pinned **exactly** by `nx@22.7.5` (same nx-pin pattern already handled with existing resolutions). - `@hono/node-server` < 2.0.5 — the consumer requires `^1.19.9`; the only fix is `2.0.5`, a **major (1.x→2.x)** bump. - `adm-zip` < 0.6.0 — pinned to `^0.5.x` by `generative-bayesian-network` (dev fingerprint tooling); 0.6.0 is a semver-major-ish jump. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index d24bb82df687..3fcbc94c6abf 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -6480,11 +6480,11 @@ __metadata: linkType: hard "brace-expansion@npm:^2.0.2": - version: 2.1.2 - resolution: "brace-expansion@npm:2.1.2" + version: 2.1.3 + resolution: "brace-expansion@npm:2.1.3" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10c0/5442ecab84045d21826268bc56c81a6ef4215327ee1f5ee153f67928e93f7a915d130ecec9966623143277fa3cce036ebb50020024bcc4d78853cdd6af9a19f8 + checksum: 10c0/62bef43c5755b4978662d66d0844635cc171610644da7d0266db8c0180c21c6e8ff7b969a27d2f2ec7893b6efd9c33dc73383d84a467669832f2da1fd3771f99 languageName: node linkType: hard From 6cb4fc392f43e7ad42d56114372eb8ad79aeff74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Thu, 30 Jul 2026 10:18:13 +0200 Subject: [PATCH 2/2] fix: respect falsy transformRequestFunction return in context enqueueLinks (#3925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `RequestTransform` is declared as `RequestOptions | false | undefined | null`, and `enqueueLinks` skips a request on any falsy return. That contract held when calling `enqueueLinks` directly, but not through `context.enqueueLinks`: the crawl depth wrapper used `options.transformRequestFunction?.(newRequest) ?? newRequest`, and `??` cannot distinguish "no transform provided" from "transform returned nullish". Returning `null` or `undefined` from a request handler enqueued the request anyway — only `false` worked as documented. Closes #3920 --- .../src/internals/basic-crawler.ts | 3 ++- packages/core/src/enqueue_links/shared.ts | 2 +- test/core/crawlers/basic_crawler.test.ts | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/basic-crawler/src/internals/basic-crawler.ts b/packages/basic-crawler/src/internals/basic-crawler.ts index 6663e784d29d..0b48a3080d95 100644 --- a/packages/basic-crawler/src/internals/basic-crawler.ts +++ b/packages/basic-crawler/src/internals/basic-crawler.ts @@ -1764,7 +1764,8 @@ export class BasicCrawler { expect(requests).toHaveLength(2); expect(requests[0]).toMatchObject({ url: 'https://example.com/1/', crawlDepth: 3 }); }); + + it.each([ + null, + undefined, + false, + ] as const)('should skip requests when transformRequestFunction returns %s', async (returnValue) => { + const transformRequestFunction = vi.fn(() => returnValue); + const optionsWithTransform = { ...options, transformRequestFunction }; + + await crawler.exposedEnqueueLinksWithCrawlDepth(optionsWithTransform, request, requestQueue); + + const requests = addRequestsBatchedMock.mock.calls[0][0]; + expect(requests).toHaveLength(0); + + const skippedRequests = onSkippedRequestMock.mock.calls.map((call) => call[0]); + expect(skippedRequests).toHaveLength(2); + expect(skippedRequests[0]).toStrictEqual({ url: 'https://example.com/1/', reason: 'filters' }); + expect(skippedRequests[1]).toStrictEqual({ url: 'https://example.com/2/', reason: 'filters' }); + }); }); it('addCrawlDepthRequestGenerator() should generate requests with maxCrawlDepth', async () => {