From f4ec00cbd59c9c235be98a5fd29d6dda7bcba498 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Thu, 3 Apr 2025 08:23:24 -0700 Subject: [PATCH 1/4] Update CodeQL query tables (#55085) Co-authored-by: Felicity Chapman --- data/reusables/code-scanning/codeql-query-tables/java.md | 1 + 1 file changed, 1 insertion(+) diff --git a/data/reusables/code-scanning/codeql-query-tables/java.md b/data/reusables/code-scanning/codeql-query-tables/java.md index a2d2f212aa47..15698c61b0ea 100644 --- a/data/reusables/code-scanning/codeql-query-tables/java.md +++ b/data/reusables/code-scanning/codeql-query-tables/java.md @@ -18,6 +18,7 @@ | [Detect JHipster Generator Vulnerability CVE-2019-16303](https://codeql.github.com/codeql-query-help/java/java-jhipster-prng/) | 338 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Disabled Netty HTTP header validation](https://codeql.github.com/codeql-query-help/java/java-netty-http-request-or-response-splitting/) | 93, 113 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Disabled Spring CSRF protection](https://codeql.github.com/codeql-query-help/java/java-spring-disabled-csrf-protection/) | 352 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | +| [Exposed Spring Boot actuators](https://codeql.github.com/codeql-query-help/java/java-spring-boot-exposed-actuators/) | 200 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Expression language injection (JEXL)](https://codeql.github.com/codeql-query-help/java/java-jexl-expression-injection/) | 094 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Expression language injection (MVEL)](https://codeql.github.com/codeql-query-help/java/java-mvel-expression-injection/) | 094 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Expression language injection (Spring)](https://codeql.github.com/codeql-query-help/java/java-spel-expression-injection/) | 094 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | From 1f11a20b6869200dc6c69b611924bd06f30e636f Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Thu, 3 Apr 2025 10:03:01 -0700 Subject: [PATCH 2/4] Look up fastly IPs using cidr (#55119) --- package-lock.json | 16 ++++++++++++-- package.json | 1 + src/shielding/lib/fastly-ips.ts | 38 ++++++++++++++++---------------- src/shielding/tests/shielding.ts | 8 +++---- 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 620bca1635db..b428ebd19ede 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "html-entities": "^2.5.2", "http-proxy-middleware": "3.0.3", "imurmurhash": "^0.1.4", + "ipaddr.js": "^2.2.0", "is-svg": "5.0.0", "javascript-stringify": "^2.1.0", "js-cookie": "^3.0.1", @@ -9261,10 +9262,12 @@ } }, "node_modules/ipaddr.js": { - "version": "1.9.1", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10" } }, "node_modules/is-alphabetical": { @@ -13062,6 +13065,15 @@ "node": ">= 0.10" } }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", diff --git a/package.json b/package.json index 8e6ef4d6b1ca..c7dd5b606166 100644 --- a/package.json +++ b/package.json @@ -286,6 +286,7 @@ "html-entities": "^2.5.2", "http-proxy-middleware": "3.0.3", "imurmurhash": "^0.1.4", + "ipaddr.js": "^2.2.0", "is-svg": "5.0.0", "javascript-stringify": "^2.1.0", "js-cookie": "^3.0.1", diff --git a/src/shielding/lib/fastly-ips.ts b/src/shielding/lib/fastly-ips.ts index f010031ec62d..142b9d933bb0 100644 --- a/src/shielding/lib/fastly-ips.ts +++ b/src/shielding/lib/fastly-ips.ts @@ -1,7 +1,11 @@ // Logic to get and store the current list of public Fastly IPs from the Fastly API: https://www.fastly.com/documentation/reference/api/utils/public-ip-list/ +import ipaddr, { IPv4, IPv6 } from 'ipaddr.js' + +type IPRangeArr = [IPv4 | IPv6, number][] + // Default returned from ➜ curl "https://api.fastly.com/public-ip-list" -export const DEFAULT_FASTLY_IPS: string[] = [ +export const DEFAULT_FASTLY_IPS: IPRangeArr = [ '23.235.32.0/20', '43.249.72.0/22', '103.244.50.0/24', @@ -21,22 +25,21 @@ export const DEFAULT_FASTLY_IPS: string[] = [ '185.31.16.0/22', '199.27.72.0/21', '199.232.0.0/16', -] +].map((cidr) => ipaddr.parseCIDR(cidr)) -let ipCache: string[] = [] +let ipRangeCache: IPRangeArr = [] -export async function getPublicFastlyIPs(): Promise { +export async function getPublicFastlyIPs(): Promise { // Don't fetch the list in dev & testing, just use the defaults if (process.env.NODE_ENV !== 'production') { - ipCache = DEFAULT_FASTLY_IPS + ipRangeCache = DEFAULT_FASTLY_IPS } - if (ipCache.length) { - return ipCache + if (ipRangeCache.length) { + return ipRangeCache } const endpoint = 'https://api.fastly.com/public-ip-list' - let ips: string[] = [] let attempt = 0 while (attempt < 3) { @@ -47,8 +50,8 @@ export async function getPublicFastlyIPs(): Promise { } const data = await response.json() if (data && Array.isArray(data.addresses)) { - ips = data.addresses - break + ipRangeCache = data.addresses.map((cidr: string) => ipaddr.parseCIDR(cidr)) + return ipRangeCache } else { throw new Error('Invalid response structure') } @@ -57,14 +60,11 @@ export async function getPublicFastlyIPs(): Promise { `Failed to fetch Fastly IPs: ${error.message}. Retrying ${3 - attempt} more times`, ) attempt++ - if (attempt >= 3) { - ips = DEFAULT_FASTLY_IPS - } } } - ipCache = ips - return ips + ipRangeCache = DEFAULT_FASTLY_IPS + return ipRangeCache } // The IPs we check in the rate-limiter are in the form `X.X.X.X` @@ -72,10 +72,10 @@ export async function getPublicFastlyIPs(): Promise { // For an IP in the rate-limiter, we want `X.X.X.*` to match `X.X.X.X/Y` export async function isFastlyIP(ip: string): Promise { // If IPs aren't initialized, fetch them - if (!ipCache.length) { + if (!ipRangeCache.length) { await getPublicFastlyIPs() } - const parts = ip.split('.') - const prefix = parts.slice(0, 3).join('.') - return ipCache.some((fastlyIP) => fastlyIP.startsWith(prefix)) + if (!ip) return false // localhost + const addr = ipaddr.parse(ip) + return ipRangeCache.some((range) => addr.match(range)) } diff --git a/src/shielding/tests/shielding.ts b/src/shielding/tests/shielding.ts index 41e4279d0815..f1932214f6ad 100644 --- a/src/shielding/tests/shielding.ts +++ b/src/shielding/tests/shielding.ts @@ -2,7 +2,6 @@ import { describe, expect, test } from 'vitest' import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key.js' import { get } from '@/tests/helpers/e2etest.js' -import { DEFAULT_FASTLY_IPS } from '@/shielding/lib/fastly-ips' describe('honeypotting', () => { test('any GET with survey-vote and survey-token query strings is 400', async () => { @@ -105,7 +104,7 @@ describe('rate limiting', () => { headers: { // Rate limiting only happens in production, so we need to // make the environment look like production. - 'fastly-client-ip': 'abc', + 'fastly-client-ip': '0.0.0.0', }, }) expect(res.statusCode).toBe(200) @@ -118,7 +117,7 @@ describe('rate limiting', () => { { const res = await get('/robots.txt?foo=buzz', { headers: { - 'fastly-client-ip': 'abc', + 'fastly-client-ip': '0.0.0.0', }, }) expect(res.statusCode).toBe(200) @@ -142,8 +141,7 @@ describe('rate limiting', () => { // Fastly IPs are in the form `X.X.X.X/Y` // Rate limited IPs are in the form `X.X.X.X` // Where the last X could be any 2-3 digit number - const mockFastlyIP = - DEFAULT_FASTLY_IPS[0].split('.').slice(0, 3).join('.') + `.${Math.floor(Math.random() * 100)}` + const mockFastlyIP = '23.235.32.0' // Cookies only allows 1 request per minute const res1 = await get('/api/cookies', { headers: { From 21068d2d46e0d16bbab4095efc6da81a0ec56964 Mon Sep 17 00:00:00 2001 From: Hector Alfaro Date: Thu, 3 Apr 2025 14:15:03 -0400 Subject: [PATCH 3/4] Bump up cpu and memory for prod (#55134) --- config/kubernetes/production/deployments/webapp.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/kubernetes/production/deployments/webapp.yaml b/config/kubernetes/production/deployments/webapp.yaml index 89f206bcded2..362ed36fb8da 100644 --- a/config/kubernetes/production/deployments/webapp.yaml +++ b/config/kubernetes/production/deployments/webapp.yaml @@ -23,10 +23,10 @@ spec: image: docs-internal resources: requests: - cpu: 2000m - memory: 6Gi - limits: cpu: 8000m + memory: 10Gi + limits: + cpu: 16000m memory: 16Gi ports: - name: http From ddb9196a69df8368821fcf44e422ccd0473c1108 Mon Sep 17 00:00:00 2001 From: Roniece Ricardo <33437850+RonRicardo@users.noreply.github.com> Date: Thu, 3 Apr 2025 14:18:57 -0400 Subject: [PATCH 4/4] Early return to temporarily disable event tracking during DDOS (#55135) --- src/events/components/events.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/events/components/events.ts b/src/events/components/events.ts index ab0861c71d94..69e0f989ae02 100644 --- a/src/events/components/events.ts +++ b/src/events/components/events.ts @@ -1,4 +1,4 @@ -/* eslint-disable camelcase */ +/* eslint-disable camelcase, no-unreachable */ import Cookies from 'src/frame/components/lib/cookies' import { parseUserAgent } from './user-agent' import { Router } from 'next/router' @@ -76,6 +76,9 @@ export function sendEvent({ } & EventPropsByType[T]) { if (isHeadless()) return + // Early return to disable event tracking + return + const body = { type,