From 0c6e51a83dacf052d5132aeebb81e4a373b13a71 Mon Sep 17 00:00:00 2001 From: Souptik Chakraborty Date: Fri, 14 Aug 2026 10:17:54 +0530 Subject: [PATCH] test(gateguard): reject Reflect access on process.env Greptile flagged that the env-access guard in gateguard-env-documented.test.js could be bypassed via reflective reads of process.env (Reflect.get/has/set/deleteProperty/defineProperty/ getOwnPropertyDescriptor/ownKeys), since none of the existing UNSUPPORTED_ACCESS patterns matched that form. Add a rule that rejects Reflect.get/has/set/deleteProperty/ defineProperty/getOwnPropertyDescriptor/ownKeys(process.env, ...) and three self-check fixture cases (Reflect.get, Reflect.has, Reflect.ownKeys) so the guard is pinned against silently missing them again. Negative control: commenting out only the new rule reproduces exactly the reported gap (the 3 new fixture cases fail with "access guard missed: Reflect.get, Reflect.has, Reflect.ownKeys"); restoring it goes back to 10/10. --- tests/ci/gateguard-env-documented.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ci/gateguard-env-documented.test.js b/tests/ci/gateguard-env-documented.test.js index 2a9da88c7..6ee96754c 100644 --- a/tests/ci/gateguard-env-documented.test.js +++ b/tests/ci/gateguard-env-documented.test.js @@ -200,6 +200,7 @@ const UNSUPPORTED_ACCESS = [ { label: 'process.env aliased to a binding', pattern: /(?:const|let|var)\s+[A-Za-z_$][\w$]*\s*=\s*process\.env\s*(?:[;,)\]]|$)/m }, { label: 'spread of process.env', pattern: /\.\.\.\s*process\.env\b/ }, { label: 'enumeration of process.env', pattern: /Object\.(?:keys|values|entries|assign|fromEntries)\(\s*process\.env\b/ }, + { label: 'Reflect access on process.env', pattern: /Reflect\.(?:get|has|set|deleteProperty|defineProperty|getOwnPropertyDescriptor|ownKeys)\(\s*process\.env\b/ }, ]; /** `process.env[...]` whose key is not a plain quoted string. */ @@ -298,6 +299,9 @@ if (test('the access guard rejects every form the parser cannot follow', () => { ['computed variable', 'const v = process.env[name];'], ['spread', 'const all = { ...process.env };'], ['enumeration', 'const ks = Object.keys(process.env);'], + ['Reflect.get', "const v = Reflect.get(process.env, 'GATEGUARD_HIDDEN');"], + ['Reflect.has', "const v = Reflect.has(process.env, 'GATEGUARD_HIDDEN');"], + ['Reflect.ownKeys', 'const ks = Reflect.ownKeys(process.env);'], ]; const missed = cases.filter(([, code]) => findUnsupportedAccess(code).length === 0).map(([label]) => label); assert.deepStrictEqual(missed, [], `access guard missed: ${missed.join(', ')}`);