jsprogram

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 26, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package jsprogram defines the deterministic, source-only semantic facts used by JavaScript/TypeScript value-flow taint. It is the JS/TS twin of pythonprogram: the model is parser-independent, tree-sitter is confined to the synapse-ast infrastructure sidecar, and every document is validated here before a use case trusts it. The differences from Python are language-level: `$` is a legal identifier character, modules are identified by their source PATH (not a dotted package name), imports carry a JS ImportKind, and symbol ids use a "js:" prefix.

Index

Constants

View Source
const (
	// SchemaVersion is the only semantic-facts wire version this build understands.
	SchemaVersion = 1
)

Variables

This section is empty.

Functions

func CanonicalSymbolID

func CanonicalSymbolID(module, qualified string) string

CanonicalSymbolID builds the stable id for a JS symbol: "js:" + module path + ":" + qualified name.

func ExternalSymbolID

func ExternalSymbolID(specifier, export string) string

ExternalSymbolID is the canonical call-graph node id for a third-party export a first-party call targets: "jsnpm:" + import specifier + ":" + export path. A consumer (a reachability analyzer) reconstructs it, or matches ExternalSymbolPrefix, to ask whether first-party code reaches a call into that package.

func ExternalSymbolPrefix

func ExternalSymbolPrefix(specifier string) string

ExternalSymbolPrefix is the id prefix shared by every external node for one import specifier, so a consumer can match all calls into a package without reconstructing each export path.

Types

type Argument

type Argument struct {
	Spread  bool      `json:"spread,omitempty"`
	Value   Reference `json:"value"`
	ValueID string    `json:"value_id,omitempty"`
	Pos     Position  `json:"position,omitempty"`
}

Argument is one positional or spread call argument. Spread values carry Spread=true.

type Assignment

type Assignment struct {
	ScopeID   string      `json:"scope_id"`
	Targets   []Reference `json:"targets"`
	TargetIDs []string    `json:"target_ids,omitempty"`
	Value     Reference   `json:"value"`
	ValueID   string      `json:"value_id,omitempty"`
	Pos       Position    `json:"position"`
}

Assignment captures a binding/value relationship without retaining expression text.

type Call

type Call struct {
	ID              string     `json:"id"`
	CallerID        string     `json:"caller_id"`
	Callee          Reference  `json:"callee"`
	Arguments       []Argument `json:"arguments,omitempty"`
	ResultID        string     `json:"result_id,omitempty"`
	ReceiverValueID string     `json:"receiver_value_id,omitempty"`
	Pos             Position   `json:"position"`
	Await           bool       `json:"await,omitempty"`
	New             bool       `json:"new,omitempty"` // a `new X(...)` constructor call
}

Call is one syntactic call (or `new`) expression owned by CallerID. Callee is a name/member reference when statically expressible, otherwise ReferenceUnknown; the extractor then also records a coverage gap (GapUnresolvedCall) so absence is not read as proof. Validate stays lenient about this pairing on purpose: a valid source file must never have its whole facts document rejected, so a missing gap is not an error.

type CallResolutionStatus

type CallResolutionStatus string

CallResolutionStatus records whether one syntactic call has a unique semantic target. Ambiguous calls retain every conservative candidate but make a negative (not-reached) proof incomplete.

const (
	CallResolved   CallResolutionStatus = "resolved"
	CallExternal   CallResolutionStatus = "external"
	CallAmbiguous  CallResolutionStatus = "ambiguous"
	CallUnresolved CallResolutionStatus = "unresolved"
)

type CoverageGap

type CoverageGap struct {
	Kind     GapKind  `json:"kind"`
	SymbolID string   `json:"symbol_id,omitempty"`
	Detail   string   `json:"detail,omitempty"`
	Pos      Position `json:"position"`
}

CoverageGap is an explicit reason a complete negative is unsafe. Detail is a trusted closed label.

type Document

type Document struct {
	SchemaVersion int              `json:"schema_version"`
	Modules       []Module         `json:"modules"`
	Symbols       []Symbol         `json:"symbols"`
	Imports       []Import         `json:"imports"`
	Calls         []Call           `json:"calls"`
	Assignments   []Assignment     `json:"assignments"`
	Returns       []Return         `json:"returns"`
	Values        []Value          `json:"values"`
	Flows         []ValueFlow      `json:"flows"`
	Entrypoints   []EntrypointHint `json:"entrypoint_hints"`
	CoverageGaps  []CoverageGap    `json:"coverage_gaps"`
	FilesSeen     int              `json:"files_seen"`
	FilesParsed   int              `json:"files_parsed"`
	NodesSeen     int              `json:"nodes_seen"`
	Truncated     bool             `json:"truncated"`
}

Document is the complete versioned facts result for one source root.

func (Document) Complete

func (d Document) Complete() bool

Complete reports whether the document can support a negative proof.

func (*Document) SortCanonical

func (d *Document) SortCanonical()

SortCanonical makes serialization and downstream graph construction independent of map/parser order.

func (Document) Validate

func (d Document) Validate() error

Validate rejects malformed or unbounded sidecar output at the trust boundary.

type EntrypointHint

type EntrypointHint struct {
	SymbolID string   `json:"symbol_id"`
	Kind     string   `json:"kind"`
	Pos      Position `json:"position"`
}

EntrypointHint is a syntactic framework/application entrypoint cue.

type GapKind

type GapKind string

GapKind is a closed reason code explaining why absence cannot be treated as proof.

const (
	GapParseRecovery    GapKind = "parse_recovery"
	GapDynamicImport    GapKind = "dynamic_import"
	GapDynamicExecution GapKind = "dynamic_execution"
	GapUnresolvedImport GapKind = "unresolved_import"
	GapUnresolvedCall   GapKind = "unresolved_call"
	GapUnresolvedValue  GapKind = "unresolved_value"
	GapDynamicAttribute GapKind = "dynamic_attribute"
	GapBudget           GapKind = "budget"
	GapUnreadable       GapKind = "unreadable"
)

func (GapKind) Valid

func (k GapKind) Valid() bool

type Import

type Import struct {
	ScopeID string     `json:"scope_id"`
	Kind    ImportKind `json:"kind"`
	Module  string     `json:"module"`
	Name    string     `json:"name,omitempty"`  // imported binding name (a named import's original name, or "default")
	Alias   string     `json:"alias,omitempty"` // the local binding introduced in this module
	Pos     Position   `json:"position"`
}

Import records one import/require binding. Module is the specifier ("express", "./util", "@scope/pkg").

type ImportKind

type ImportKind string

ImportKind records how a binding entered the module. Together with Module (the specifier) it lets the taint catalog match a sink/source to its package without resolving the module graph.

const (
	ImportNamed     ImportKind = "named"     // import { a as b } from 'm'
	ImportDefault   ImportKind = "default"   // import d from 'm'
	ImportNamespace ImportKind = "namespace" // import * as ns from 'm'
	ImportRequire   ImportKind = "require"   // const x = require('m')
	ImportReexport  ImportKind = "reexport"  // export ... from 'm'
)

func (ImportKind) Valid

func (k ImportKind) Valid() bool

type Module

type Module struct {
	Name string   `json:"name"`
	File string   `json:"file"`
	Pos  Position `json:"position"`
}

Module associates a canonical module candidate (its source path without extension) with its source file.

type Parameter

type Parameter struct {
	Name    string        `json:"name"`
	Kind    ParameterKind `json:"kind"`
	ValueID string        `json:"value_id,omitempty"`
	Pos     Position      `json:"position"`
}

Parameter is one callable parameter in declaration order.

type ParameterKind

type ParameterKind string

ParameterKind preserves JS/TS argument-binding semantics without carrying annotations/default text.

const (
	ParameterPositional   ParameterKind = "positional"
	ParameterRest         ParameterKind = "rest"         // ...args
	ParameterDestructured ParameterKind = "destructured" // {a}/[a] pattern-bound name
	ParameterDefault      ParameterKind = "default"      // a = expr
)

func (ParameterKind) Valid

func (k ParameterKind) Valid() bool

type Position

type Position struct {
	File   string `json:"file"`
	Line   int    `json:"line"`
	Column int    `json:"column"`
}

Position is a normalized, relative source location. Column is zero-based, Line is one-based.

type Reference

type Reference struct {
	Kind     ReferenceKind `json:"kind"`
	Segments []string      `json:"segments,omitempty"`
}

Reference is a safe expression summary such as ["req", "query", "id"]. No source body or literal value is retained. Literal references only carry Kind, not their value.

type ReferenceKind

type ReferenceKind string

ReferenceKind describes a bounded expression shape. It intentionally excludes arbitrary source text.

const (
	ReferenceName       ReferenceKind = "name"
	ReferenceAttribute  ReferenceKind = "attribute" // member access a.b.c
	ReferenceCall       ReferenceKind = "call"
	ReferenceExpression ReferenceKind = "expression"
	ReferenceLiteral    ReferenceKind = "literal"
	ReferenceUnknown    ReferenceKind = "unknown"
)

func (ReferenceKind) Valid

func (k ReferenceKind) Valid() bool

type Resolution

type Resolution struct {
	Graph    callgraph.Graph `json:"-"`
	Calls    []ResolvedCall  `json:"calls"`
	Gaps     []CoverageGap   `json:"coverage_gaps"`
	Complete bool            `json:"complete"`
}

Resolution is the pure JS/TS call-graph result. It is the twin of pythonprogram.Resolution: Graph is useful for POSITIVE (reached) evidence even when Complete is false, but a caller must require Complete before treating the absence of a path as a negative proof. Complete is false whenever the extractor left a coverage gap (a dynamic construct: computed member, eval/Function, dynamic import, with, an unresolved or ambiguous call), which is what keeps a JS negative sound in the presence of dynamic dispatch.

func Resolve

func Resolve(document Document) (Resolution, error)

Resolve builds a deterministic, conservative JS/TS call graph from the source-only facts, with no filesystem or interpreter I/O. It mirrors pythonprogram.Resolve: it resolves what is unambiguous (lexical functions, class methods via receiver typing and this/super, first-party relative imports) and records a coverage gap for anything it cannot pin (an ambiguous or unresolved callee), so the Complete flag stays false unless every call was resolved over a fully-parsed tree.

type ResolvedCall

type ResolvedCall struct {
	CallID   string               `json:"call_id"`
	CallerID string               `json:"caller_id"`
	Callees  []string             `json:"callees,omitempty"`
	Status   CallResolutionStatus `json:"status"`
	Pos      Position             `json:"position"`
}

ResolvedCall connects a source call fact to its deterministic semantic candidates.

type Return

type Return struct {
	ScopeID string    `json:"scope_id"`
	Value   Reference `json:"value"`
	ValueID string    `json:"value_id,omitempty"`
	SlotID  string    `json:"slot_id,omitempty"`
	Pos     Position  `json:"position"`
}

Return captures a function return expression summary.

type Symbol

type Symbol struct {
	ID            string      `json:"id"`
	Module        string      `json:"module"`
	QualifiedName string      `json:"qualified_name"`
	Name          string      `json:"name"`
	ParentID      string      `json:"parent_id,omitempty"`
	Kind          SymbolKind  `json:"kind"`
	Pos           Position    `json:"position"`
	Parameters    []Parameter `json:"parameters,omitempty"`
	Decorators    []Reference `json:"decorators,omitempty"`
	Bases         []Reference `json:"bases,omitempty"` // the class's `extends` clause (at most one in JS)
	Async         bool        `json:"async,omitempty"`
}

Symbol is a module, class, function, method, or arrow declaration.

type SymbolKind

type SymbolKind string

SymbolKind identifies a JS/TS declaration that owns a lexical scope or can appear in a call graph.

const (
	SymbolModule   SymbolKind = "module"
	SymbolClass    SymbolKind = "class"
	SymbolFunction SymbolKind = "function"
	SymbolMethod   SymbolKind = "method"
	SymbolArrow    SymbolKind = "arrow" // the JS analog of a Python lambda (arrow / anonymous function expression)
)

func (SymbolKind) Valid

func (k SymbolKind) Valid() bool

type Value

type Value struct {
	ID      string    `json:"id"`
	ScopeID string    `json:"scope_id"`
	Kind    ValueKind `json:"kind"`
	Name    string    `json:"name,omitempty"`
	Ref     Reference `json:"reference"`
	Pos     Position  `json:"position"`
}

Value is one stable value slot. Ref carries only a bounded semantic shape; source text is never kept.

type ValueFlow

type ValueFlow struct {
	FromID string        `json:"from_id"`
	ToID   string        `json:"to_id"`
	Kind   ValueFlowKind `json:"kind"`
	Pos    Position      `json:"position"`
}

ValueFlow says a value can propagate from one slot to another inside the same JS function.

type ValueFlowKind

type ValueFlowKind string

ValueFlowKind is a closed intra-procedural propagation operation emitted by the sidecar.

const (
	FlowExpression ValueFlowKind = "expression"
	FlowAttribute  ValueFlowKind = "attribute"
	FlowAssignment ValueFlowKind = "assignment"
	FlowReturn     ValueFlowKind = "return"
)

func (ValueFlowKind) Valid

func (k ValueFlowKind) Valid() bool

type ValueKind

type ValueKind string

ValueKind identifies a source-level value slot used by the interprocedural taint engine.

const (
	ValueParameter  ValueKind = "parameter"
	ValueBinding    ValueKind = "binding"
	ValueReference  ValueKind = "reference"
	ValueCallResult ValueKind = "call_result"
	ValueExpression ValueKind = "expression"
	ValueLiteral    ValueKind = "literal"
	ValueReturn     ValueKind = "return"
)

func (ValueKind) Valid

func (k ValueKind) Valid() bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL