[GH-ISSUE #3591] [Feature] Compare static JavaScript endpoints with observed HAR API-surface groups #1332

Open
opened 2026-10-16 21:04:22 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @Y0UW1SH on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3591

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Evidence, artifacts, or reconstruction

Current limitation

Analyst question: "Which API endpoints does this client's code reference that my capture never exercised, and which captured requests did static analysis miss?"

Source-derived (commit 1579b68):

  • analyze_javascript_application emits literal request targets as Application Graph endpoint nodes (value, mechanism, location) through endpointArgument. No HTTP method is kept (JavaScriptStaticEndpoint). Values of overloaded method names become low-confidence interpretation: "candidate" nodes (addEndpoint).
  • inspect_analysis_view api-surface groups one HAR parent by method, origin and path template (docs).
  • reconcile_javascript_runtime matches scripts, frames and workers, not endpoints (docs). correlate_static_and_runtime only evaluates caller-declared hypotheses.

So an agent pages both results and joins them by eye. That costs tokens and invites over-claiming: a path missing from a short capture gets called a "hidden API", or candidate-only values get listed as unexercised endpoints.

Use cases

Web or Electron client analysis with both the shipped JavaScript and a HAR recorded while using the app: deciding which static endpoints still need a capture scenario, and which captured groups static discovery missed (string-built, wrapper-passed or server-supplied URLs).

Desired outcome and success criteria

One read-only call over existing analyze_javascript_application and HAR Evidence returns a status and basis for every static network endpoint and every observed group:

{ "summary": { "static_network_endpoints": 3, "matched": 1, "not_observed_in_capture": 1, "unknown": 1, "observed_without_static_match": 1 },
  "static_endpoints": [
    { "path": "/rest/user/login", "status": "matched", "match_basis": "exact",
      "observed": [{ "method": "POST", "path_template": "/rest/user/login", "record_ordinals": [4, 9] }] },
    { "path": "/api/admin/export", "status": "not-observed-in-capture" },
    { "mechanism": "call:translate.get", "status": "unknown", "reason": "candidate-endpoint" } ],
  "observed_without_static_match": [{ "method": "GET", "path_template": "/v2/feed/{uuid}", "record_ordinals": [12] }],
  "coverage": { "observed_records_examined": 140, "observed_records_excluded": 0 } }
  • Every pairing names its basis (exact or template-compatible); anything else is ambiguous or unknown with a reason. not-observed-in-capture requires an exercised origin and no excluded parent records.
  • Candidate endpoints and GraphQL/tRPC operation names are never reported as matched or not observed.
  • No query strings, fragments, headers or bodies are copied; existing api-surface output is unchanged.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Non-goals: no requests to any endpoint (no probing, replay or enumeration); no claim that "not observed" means unused or hidden; HAR only in v1 (SAZ/mitmproxy after #2884 / #2915); no analyze_web_bundle input in v1.

Dependency: the comparison is only as complete as static discovery. #3451 / #3534 (template-literal, variable-base and URL-object targets), #3539 (request-wrapper URLs) and the merged #3042 / #3044 (EventSource and sendBeacon URLs) are that static side; the value of this feature grows as the open ones land. Design questions for maintainers (read-only, no new provider): a new compare-shaped tool or an extension of reconcile_javascript_runtime? Should the observed side share group identity with #2914?

Verification available so far

I can suggest a sample or manual check

Verification details (optional)

Proposed, not run: a self-authored fixture app (fetch('/api/users'), http.post('/rest/login'), a string-built URL, translate.get('KEY')) plus a HAR from tests/fixtures/historicalHar.ts, expecting one exact match, one not-observed, one candidate unknown. Then the mitmproxy-authored HAR in the real web-network-captures lane through CLI and MCP. Edge cases: encoded %2e%2e paths, method disagreement, multi-origin ambiguity, ordinal-subset and sensitive_values parents.

Alternatives considered

  • Paging api-surface and the application graph and joining by hand: token-heavy and easy to over-claim.
  • reconcile_javascript_runtime: maps runtime script/frame/worker instances into a combined graph and does not take HAR Evidence.
  • correlate_static_and_runtime: needs one declared hypothesis per pair. A new view kind: views project a single parent.

Possible direction

A proposal, not a requirement:

  • A compare-shaped tool (proposed compare_static_and_observed_endpoints) taking inline or same-session retained Evidence, like compare_application_versions.
  • Share group materialization with WebNetworkApiSurfaceAccumulator so the view, this tool and #2914 use one templating owner.
  • Ordered rules: eligibility, method (callee suffix; EventSource GET and sendBeacon POST by API definition), exact, template-compatible, then ambiguous.
  • Caller-declared origins for origin-relative paths (labelled inferred); reuse the sensitive_values_unretained refusal and 96 MiB budget.

References

Originally created by @Y0UW1SH on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3591 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Current limitation Analyst question: "Which API endpoints does this client's code reference that my capture never exercised, and which captured requests did static analysis miss?" Source-derived (commit 1579b68): - `analyze_javascript_application` emits literal request targets as Application Graph `endpoint` nodes (`value`, `mechanism`, location) through [`endpointArgument`](https://github.com/morluto/rea/blob/1579b68/src/domain/javascript/javascriptStaticAnalysisHelpers.ts#L594). No HTTP method is kept ([`JavaScriptStaticEndpoint`](https://github.com/morluto/rea/blob/1579b68/src/domain/javascript/javascriptStaticAnalysisTypes.ts#L44)). Values of overloaded method names become low-confidence `interpretation: "candidate"` nodes ([`addEndpoint`](https://github.com/morluto/rea/blob/1579b68/src/application/javascript/JavaScriptArtifactGraphFindings.ts#L192)). - `inspect_analysis_view` `api-surface` groups one HAR parent by method, origin and path template ([docs](https://github.com/morluto/rea/blob/1579b68/docs/web-network-captures.md#derived-api-surface)). - `reconcile_javascript_runtime` matches scripts, frames and workers, not endpoints ([docs](https://github.com/morluto/rea/blob/1579b68/docs/javascript-runtime-reconciliation.md)). `correlate_static_and_runtime` only evaluates caller-declared hypotheses. So an agent pages both results and joins them by eye. That costs tokens and invites over-claiming: a path missing from a short capture gets called a "hidden API", or candidate-only values get listed as unexercised endpoints. ### Use cases Web or Electron client analysis with both the shipped JavaScript and a HAR recorded while using the app: deciding which static endpoints still need a capture scenario, and which captured groups static discovery missed (string-built, wrapper-passed or server-supplied URLs). ### Desired outcome and success criteria One read-only call over existing `analyze_javascript_application` and HAR Evidence returns a status and basis for every static network endpoint and every observed group: ```json { "summary": { "static_network_endpoints": 3, "matched": 1, "not_observed_in_capture": 1, "unknown": 1, "observed_without_static_match": 1 }, "static_endpoints": [ { "path": "/rest/user/login", "status": "matched", "match_basis": "exact", "observed": [{ "method": "POST", "path_template": "/rest/user/login", "record_ordinals": [4, 9] }] }, { "path": "/api/admin/export", "status": "not-observed-in-capture" }, { "mechanism": "call:translate.get", "status": "unknown", "reason": "candidate-endpoint" } ], "observed_without_static_match": [{ "method": "GET", "path_template": "/v2/feed/{uuid}", "record_ordinals": [12] }], "coverage": { "observed_records_examined": 140, "observed_records_excluded": 0 } } ``` - Every pairing names its basis (`exact` or `template-compatible`); anything else is `ambiguous` or `unknown` with a reason. `not-observed-in-capture` requires an exercised origin and no excluded parent records. - Candidate endpoints and GraphQL/tRPC operation names are never reported as matched or not observed. - No query strings, fragments, headers or bodies are copied; existing `api-surface` output is unchanged. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) Non-goals: no requests to any endpoint (no probing, replay or enumeration); no claim that "not observed" means unused or hidden; HAR only in v1 (SAZ/mitmproxy after #2884 / #2915); no `analyze_web_bundle` input in v1. Dependency: the comparison is only as complete as static discovery. #3451 / #3534 (template-literal, variable-base and URL-object targets), #3539 (request-wrapper URLs) and the merged #3042 / #3044 (EventSource and sendBeacon URLs) are that static side; the value of this feature grows as the open ones land. Design questions for maintainers (read-only, no new provider): a new compare-shaped tool or an extension of `reconcile_javascript_runtime`? Should the observed side share group identity with #2914? ### Verification available so far I can suggest a sample or manual check ### Verification details (optional) Proposed, not run: a self-authored fixture app (`fetch('/api/users')`, `http.post('/rest/login')`, a string-built URL, `translate.get('KEY')`) plus a HAR from `tests/fixtures/historicalHar.ts`, expecting one exact match, one not-observed, one candidate unknown. Then the mitmproxy-authored HAR in the real web-network-captures lane through CLI and MCP. Edge cases: encoded `%2e%2e` paths, method disagreement, multi-origin ambiguity, ordinal-subset and `sensitive_values` parents. ### Alternatives considered - Paging `api-surface` and the application graph and joining by hand: token-heavy and easy to over-claim. - `reconcile_javascript_runtime`: maps runtime script/frame/worker instances into a combined graph and does not take HAR Evidence. - `correlate_static_and_runtime`: needs one declared hypothesis per pair. A new view kind: views project a single parent. ### Possible direction A proposal, not a requirement: - A compare-shaped tool (proposed `compare_static_and_observed_endpoints`) taking inline or same-session retained Evidence, like `compare_application_versions`. - Share group materialization with `WebNetworkApiSurfaceAccumulator` so the view, this tool and #2914 use one templating owner. - Ordered rules: eligibility, method (callee suffix; `EventSource` GET and `sendBeacon` POST by API definition), exact, template-compatible, then ambiguous. - Caller-declared `origins` for origin-relative paths (labelled `inferred`); reuse the `sensitive_values_unretained` refusal and 96 MiB budget. ### References - Builds on https://github.com/morluto/rea/pull/1891; related https://github.com/morluto/rea/issues/2914; `compare` intent: https://github.com/morluto/rea/blob/1579b68/docs/tool-design.md - Static side: https://github.com/morluto/rea/issues/3451, https://github.com/morluto/rea/pull/3534, https://github.com/morluto/rea/issues/3539, https://github.com/morluto/rea/issues/3042, https://github.com/morluto/rea/pull/3044 - Endpoint noise: https://github.com/morluto/rea/issues/2806, https://github.com/morluto/rea/pull/2878. Other formats: https://github.com/morluto/rea/pull/2884, https://github.com/morluto/rea/issues/2915. Adjacent HAR preview work: https://github.com/morluto/rea/pull/3293
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1332
No description provided.