Conversation
…bleNetConnect() in mocha-bootstrap.js (b/566321794)
There was a problem hiding this comment.
Code Review
This pull request disables external network connections globally in the Mocha bootstrap helper using nock.disableNetConnect() to prevent unit tests from reaching real Google APIs. A review comment suggests also re-applying these restrictions inside the global cleanup() function to prevent state leakage, as nock.cleanAll() does not reset the net-connect configuration if individual tests explicitly enable it.
…egration test suites to access network
ajperel
left a comment
There was a problem hiding this comment.
⚠️ Disclaimer: This draft review was generated by an experimental AI review agent. Please verify all findings before acting on them.
Code Review Summary: firebase/firebase-tools #11178
🟢 Strengths & LGTM Aspects
- Fail-Fast Network Isolation: Disabling external outbound HTTP/HTTPS calls globally via
nock.disableNetConnect()provides immediate, deterministic failure (NetConnectNotAllowedError) instead of waiting for 2000ms timeouts or flaky ECONNRESET retry loops when unit tests fail to mock a Google API. - Localhost & Emulator Traffic Allowed: The regex filter correctly preserves loopback traffic (
localhost,127.0.0.1,[::1],::1), ensuring in-process or local emulator servers continue to work seamlessly. - Integration Test Exclusion: Correctly distinguishes integration test suites running out of
scripts/anddev/scripts/that legitimately require real outbound network connectivity. - Minimal, Surgical Footprint: The change is clean, self-contained within
src/test/helpers/mocha-bootstrap.js, and introduces zero overhead or new dependencies to production CLI commands. - Changelog Governance: Appropriately omits a
CHANGELOG.mdentry as this is an internal test infrastructure enhancement.
🔴 Overview of Findings
- Blocking [Test Isolation & State Leakage]:
nock.cleanAll()incleanup()does not resetnock's net-connect state. 15 existing test suites in the repository explicitly callnock.enableNetConnect()in theirafter()hooks, which leaves outbound network access permanently open for subsequent suites unless re-asserted in Mocha lifecycle hooks (beforeEach/afterEach).
💡 Potential Enhancements
- Preventing Unscoped
enableNetConnect(): Complementing thebeforeEachreset by guarding against unscoped calls at runtime inmocha-bootstrap.js(throwing or clamping to loopback), updatingsrc/test/helpers/nock.ts, and adding an ESLintno-restricted-syntaxrule to enforce scoping statically.
🟡 Nits & Non-Blocking Suggestions
- Loopback Constant: Extract
/^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/to a named top-level constant. - Environment Variable Escape Hatch: Check
process.env.FIREBASE_ALLOW_NET_CONNECT === "true"for ad-hoc debugging in custom IDE runners.
| ); | ||
| if (!isIntegrationTest) { | ||
| nock.disableNetConnect(); | ||
| nock.enableNetConnect(/^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/); |
There was a problem hiding this comment.
🔴 [Test Isolation & State Leakage] Re-assert net-connect restriction in Mocha hooks (beforeEach / afterEach)
Rationale:
nock.cleanAll() in cleanup() (afterEach) removes active HTTP interceptors, but does not reset nock's net-connect configuration. If any test or suite explicitly calls nock.enableNetConnect(), that unrestricted state persists across all subsequent tests in the same Mocha worker process.
Furthermore, 15 existing test suites in the repository (e.g., src/accountImporter.spec.ts, src/gcp/cloudfunctions.spec.ts, src/management/apps.spec.ts, src/ensureApiEnabled.spec.ts, etc.) contain:
after(() => {
nock.enableNetConnect();
});In Mocha, a suite's after() hook executes after the afterEach (cleanup()) hook of the suite's final test. If net-connect is only restricted once at bootstrap or only inside afterEach, any suite running after one of these files will execute its before() and initial test case with external network access wide open.
Suggested Fix:
Encapsulate the policy enforcement in a helper and re-apply it both during initial load and in beforeEach (as well as cleanup()):
const LOOPBACK_REGEXP = /^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/;
function enforceNetConnectPolicy() {
if (!isIntegrationTest) {
nock.disableNetConnect();
nock.enableNetConnect(LOOPBACK_REGEXP);
}
}
enforceNetConnectPolicy();And in exports.mochaHooks:
exports.mochaHooks = {
beforeEach() {
enforceNetConnectPolicy();
suiteFakes = new Set(typeof sinon.getFakes === "function" ? sinon.getFakes() : []);
},
afterEach: cleanup,
};| ); | ||
| if (!isIntegrationTest) { | ||
| nock.disableNetConnect(); | ||
| nock.enableNetConnect(/^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/); |
There was a problem hiding this comment.
💡 [Enhancement] Guard against unscoped enableNetConnect() calls
Rationale:
Re-asserting the restriction in beforeEach protects across test boundaries. However, tests could still call nock.enableNetConnect() without arguments inside a test body, inadvertently opening outbound network access during execution.
Suggested Approaches:
-
Runtime Monkey-Patch in
mocha-bootstrap.js:
Wrapnock.enableNetConnectso that any unscoped invocation either throws an explicit error (fail-fast) or automatically clamps to the loopback regex:const origEnableNetConnect = nock.enableNetConnect.bind(nock); if (!isIntegrationTest) { nock.enableNetConnect = function (matcher) { const isUnscoped = !matcher || matcher === ".*" || matcher === "*" || (matcher instanceof RegExp && (matcher.source === ".*" || matcher.source === "^.*$")); if (isUnscoped) { // Option A: Fail fast throw new Error( "Calling unscoped nock.enableNetConnect() in unit tests is prohibited. " + "Specify an explicit host pattern or mock the network request." ); // Option B: Silently clamp to loopback // return origEnableNetConnect(LOOPBACK_REGEXP); } return origEnableNetConnect(matcher); }; }
-
Guard the Undici Mock Wrapper (
src/test/helpers/nock.ts):
The 15 existing spec files callingnock.enableNetConnect()actually importsrc/test/helpers/nock.ts(which delegates to Undici'smockAgent.enableNetConnect()). GuardingenableNetConnect(matcher?)there prevents leaks through the customfetchmocking layer as well. -
Static Prevention via ESLint:
Add ano-restricted-syntaxrule to.eslintrc.jsto catch zero-argumentnock.enableNetConnect()calls at lint time before tests run:{ selector: "CallExpression[callee.object.name='nock'][callee.property.name='enableNetConnect'][arguments.length=0]", message: "Calling unscoped nock.enableNetConnect() in unit tests is forbidden. Pass an explicit host pattern or mock the request." }
ajperel
left a comment
There was a problem hiding this comment.
It'd be a bigger change but you could try to clean up all the existing usages of enableNetConnect() and enforce better behavior in one PR if you want. But even the best effort options is a good start.
…d enableNetConnect
|
Thank you for the review and suggestions @ajperel! In commit 1a4b10f:
Verified locally with |
… var escape hatch
|
Thanks @ajperel! Addressed all the points in commit df099ad:
Verified locally with |
ajperel
left a comment
There was a problem hiding this comment.
Joe agent ... in another PR can you track the work to not call overly broad enableNetConnect() in other tests and making a good decision on if we should monkey patch and generally prevent it.
|
Thanks @ajperel! In commit 17716a8:
|
Resolves Buganizer b/566321794 (Parent Goal: b/566303602)
Proposed Improvement
nock.disableNetConnect()insrc/test/helpers/mocha-bootstrap.jsto fail fast withNetConnectNotAllowedErrorwhenever a unit test initiates un-mocked outbound HTTPS requests to Google APIs (e.g.firebase.googleapis.com,compute.googleapis.com,cloudresourcemanager.googleapis.com).nock.enableNetConnect(/^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/).Verification
npm run mocha:fastpasses all 5,566 unit tests (5566 passing, 11 pending, 0 failing).npm run lint:quietpasses cleanly across the entire codebase.npm run buildcompiles cleanly.