Skip to content

fix: materialize named object-literal methods so Jelly edges never dangle #57

Description

@rahlk

Problem

The Jelly provider can assign a stable-looking signature to a named object-literal method, mark it as a real callable (synth: false), and emit call edges for it even though the syntactic symbol table never materializes object-literal methods. The resulting edge references an endpoint absent from all three node registries:

  • recursively nested symbol_table callables;
  • external_symbols;
  • synthesized_callables.

Neo4j emission uses MATCH for both endpoint signatures, so these relationships are silently dropped.

This is the named-object-method continuation of #13, which fixed anonymous callback endpoints only.

Reproduction

OWASP Juice Shop 6244c59, level-2 union graph:

  • 17 undeclared endpoint IDs
  • 30 affected CALL_DEP edges

The undeclared endpoints are:

  • 11 Cypress task object methods under cypress.config.setupNodeEvents.* (GenerateCoupon, GetBlueprint, GetChristmasProduct, GetFromConfig, GetFromMemories, GetOverwriteUrl, GetPastebinLeakProduct, GetTamperingProductId, isDocker, isWindows, toISO8601);
  • 2 object methods named resolved in hacking-instructor challenge objects;
  • 4 Sequelize model object-literal setters named set.

Repeated names are also conflated. For example, multiple resolved() or set() implementations in one enclosing callable collapse onto one signature, losing location identity and aggregating unrelated outgoing calls.

One additional Cypress object method, GenerateAuthenticator, calls imported generateSync() but produces neither a callable node nor an edge.

Root cause

In src/semantic_analysis/jellyProvider.ts:

  • signatureFor() calls computeSignatureForDecl() for a function-like node;
  • named object-literal methods can receive a non-null signature and therefore return synth: false;
  • recordIfSynth() only materializes nodes when synth === true.

But src/syntactic_analysis/builders.ts collects module functions and class/interface methods, not arbitrary object-literal methods. Therefore a signature being "real" to Jelly does not imply a corresponding symbol-table node exists.

Expected behavior

Every emitted call edge endpoint must have exactly one materialized identity, and distinct object-literal method declarations must remain distinct.

Acceptance criteria

  • Enforce the invariant for every emitted edge:
    source,target ∈ recursive symbol-table callable IDs ∪ external_symbols ∪ synthesized_callables.
  • Materialize named object-literal methods with project-relative file and source location.
  • Do not conflate repeated property names such as set or resolved; include sufficient enclosing scope/location in the ID.
  • Preserve stable IDs across eager/lazy runs.
  • Add regressions for:
    • a named object method calling a first-party function;
    • two same-named methods in different object literals;
    • a Sequelize-style set() method;
    • JSON/Neo4j call-edge parity when these methods are present.
  • Fail validation rather than silently dropping relationships if an endpoint invariant is violated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixBug fix

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions