Skip to content

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

Open
apappas1129 wants to merge 2 commits 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 2 commits 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
@DeusData DeusData added bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency. labels Jul 7, 2026
@DeusData

DeusData commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Thanks for the native fetch fix for #856. Triage: parsing/quality bug, normal priority.

Review focus is the shadowing boundary: bare global fetch should classify as HTTP_CALLS, but local functions or variables named fetch must not become outbound HTTP edges. The split between service pattern classification and local resolution is the important part here.

@DeusData DeusData added this to the 0.9.1-rc milestone Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency.

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)

2 participants