Skip to content

fix(pipeline): classify native fetch() as HTTP_CALLS, not local shadows#927

Open
apappas1129 wants to merge 1 commit into
DeusData:mainfrom
apappas1129:fix/856-native-fetch-detection
Open

fix(pipeline): classify native fetch() as HTTP_CALLS, not local shadows#927
apappas1129 wants to merge 1 commit into
DeusData:mainfrom
apappas1129:fix/856-native-fetch-detection

Conversation

@apappas1129

Copy link
Copy Markdown
Contributor

What does this PR do?

Closes #856: native fetch() calls (Node 18+'s built-in, and the identical browser/WHATWG API — no static way to tell them apart, and no need to: both make a real outbound HTTP request) were invisible to cross-repo intelligence. A bare fetch(url) has no import and typically no local definition, so registry resolution comes back empty and the call was silently dropped instead of becoming an HTTP_CALLS edge.

The obvious fix — add "fetch" to the http_libraries substring table in service_patterns.c — is wrong: that table is consulted by an early, unconditional check in both pass_calls.c and pass_parallel.c that runs before/regardless of whether the callee resolves to a real local definition (by design, so axios.get(url) still classifies even when the registry mis-binds bare get to an unrelated local method — the #523 bypass). That's safe for "axios"/"requests" because nobody names their own function that; it is not safe for "fetch", which collides with a plausible local identifier (function fetch(){} or const fetch = () => {}).

Instead, cbm_service_pattern_is_global_fetch() is a new, narrow, exact-match check consulted only in the empty-resolution fallback in both files — the existing #523 path for unindexed external libraries. By the time that branch runs, the registry has already had its chance to resolve "fetch" to a local/imported definition; only a genuine miss reaches the new check. This also means a member call like repo.fetch() is naturally excluded — its callee_name is "repo.fetch", not the bare "fetch" this checks for.

pass_parallel.c's empty-resolution branch calls the low-level emit_http_async_service_edge() directly rather than going through emit_service_edge(), which re-derives its own classification from res->qualified_name via cbm_service_pattern_match() — a call with "fetch" would come back CBM_SVC_NONE there and silently fall through to a plain CALLS edge, quietly losing the fix.

Three new tests in test_pipeline.c:

  • bare fetch()HTTP_CALLS, sequential path (< 50 files)
  • bare fetch()HTTP_CALLS, forced through the parallel resolver (≥ 50 files) — the two call-resolution implementations are independent, so both need direct coverage
  • a local function fetch(){} shadowing the global → plain CALLS to the local definition, zero HTTP_CALLS (the false-positive this PR must not introduce), bundled with a repo.fetch() member-call check in the first test

Refs #592.

Checklist

  • Every commit is signed off (git commit -s)
  • Tests pass locally (make -f Makefile.cbm test) — full suite: 5936 passed, 1 skipped (pre-existing, unrelated), 0 failed
  • Lint passes (make -f Makefile.cbm lint-ci) — clang-format clean on all touched files; lint-cppcheck's pre-existing failures (extract_defs.c, compat_regex.c) reproduce identically on unmodified main (verified via stash/pop), confirming they predate this change and aren't introduced by it
  • New behavior is covered by a test (reproduce-first for bug fixes)

Closes DeusData#856: native `fetch()` calls (Node 18+ built-in, and the identical
browser/WHATWG API — no static way to tell them apart, and no need to:
both make a real outbound HTTP request) were invisible to cross-repo
intelligence. A bare `fetch(url)` has no import and typically no local
definition, so registry resolution comes back empty and the call was
silently dropped instead of becoming an HTTP_CALLS edge.

The obvious fix — add "fetch" to the http_libraries substring table in
service_patterns.c — is wrong: that table is consulted by an early,
unconditional check in both pass_calls.c and pass_parallel.c that runs
before/regardless of whether the callee resolves to a real local
definition (by design, so `axios.get(url)` still classifies even when the
registry mis-binds bare `get` to an unrelated local method). That's safe
for "axios"/"requests" because nobody names their own function that; it
is not safe for "fetch", which collides with a plausible local identifier
(`function fetch(){}` or `const fetch = () => {}`).

Instead, cbm_service_pattern_is_global_fetch() is a new, narrow, exact-
match check consulted only in the *empty-resolution* fallback in both
files — the existing DeusData#523 path for unindexed external libraries. By the
time that branch runs, the registry has already had its chance to
resolve "fetch" to a local/imported definition; only a genuine miss
reaches the new check. This also means a member call like `repo.fetch()`
is naturally excluded (its callee_name is "repo.fetch", not the bare
"fetch" this checks for).

pass_parallel.c's empty-resolution branch calls the low-level
emit_http_async_service_edge() directly rather than going through
emit_service_edge(), which re-derives its own classification from
res->qualified_name via cbm_service_pattern_match() — a call with
"fetch" would come back CBM_SVC_NONE there and silently fall through to
a plain CALLS edge.

Three new tests in test_pipeline.c:
- bare fetch() -> HTTP_CALLS, sequential path (< 50 files)
- bare fetch() -> HTTP_CALLS, forced through the parallel resolver
  (>= 50 files)
- a local `function fetch(){}` shadowing the global -> plain CALLS to
  the local definition, zero HTTP_CALLS (the false-positive this PR
  must not introduce), bundled with a `repo.fetch()` member-call check
  in the first test

Verified end-to-end, not just by inspection: prod build clean under
-Wall -Wextra -Werror; test build clean under ASan+UBSan; full suite
5936 passed, 1 skipped (pre-existing, unrelated), 0 failed; clang-format
clean on all touched files; lint-cppcheck's pre-existing failures
(extract_defs.c, compat_regex.c) reproduced identically on unmodified
main via stash/pop, confirming they predate this change.

Refs DeusData#592.

Signed-off-by: Alexandros Pappas <11921291+apappas1129@users.noreply.github.com>
@apappas1129 apappas1129 requested a review from DeusData as a code owner July 7, 2026 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP client detection misses native fetch() — only matches hardcoded library allowlist (axios/node-fetch/ofetch/etc)

1 participant