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.
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:symbol_tablecallables;external_symbols;synthesized_callables.Neo4j emission uses
MATCHfor 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:CALL_DEPedgesThe undeclared endpoints are:
cypress.config.setupNodeEvents.*(GenerateCoupon,GetBlueprint,GetChristmasProduct,GetFromConfig,GetFromMemories,GetOverwriteUrl,GetPastebinLeakProduct,GetTamperingProductId,isDocker,isWindows,toISO8601);resolvedin hacking-instructor challenge objects;set.Repeated names are also conflated. For example, multiple
resolved()orset()implementations in one enclosing callable collapse onto one signature, losing location identity and aggregating unrelated outgoing calls.One additional Cypress object method,
GenerateAuthenticator, calls importedgenerateSync()but produces neither a callable node nor an edge.Root cause
In
src/semantic_analysis/jellyProvider.ts:signatureFor()callscomputeSignatureForDecl()for a function-like node;synth: false;recordIfSynth()only materializes nodes whensynth === true.But
src/syntactic_analysis/builders.tscollects 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
source,target ∈ recursive symbol-table callable IDs ∪ external_symbols ∪ synthesized_callables.setorresolved; include sufficient enclosing scope/location in the ID.set()method;