Call Graph
Qryon generates call graphs to visualize function relationships with security context, helping you understand how data flows through your codebase.
What is a Call Graph?
A call graph is a directed graph showing which functions call which other functions. Qryon enhances this with security annotations, marking functions as sources, sinks, or sanitizers based on their security role.
Security Classifications
| Classification | Color | Description |
|---|---|---|
| Source | Green | Functions that introduce untrusted data |
| Sink | Red | Functions that perform sensitive operations |
| Sanitizer | Blue | Functions that clean or validate data |
| Propagator | Yellow | Functions that pass data through |
| Neutral | Gray | Functions without security significance |
Generating Call Graphs
# Generate call graph during scan
rma scan . --call-graph
# View in interactive TUI
rma scan --interactive
# Press 'g' to switch to call graph tab
# Export call graph
rma scan . --call-graph --format dot > callgraph.dot
rma scan . --call-graph --format json > callgraph.json
# Generate SVG visualization
rma scan . --call-graph --format dot | dot -Tsvg > callgraph.svgInteractive TUI Features
The TUI provides an interactive call graph explorer with the following controls:
| Key | Action |
|---|---|
| j/k | Navigate between functions |
| Enter | View function details and callers/callees |
| h | Collapse to callers only |
| l | Expand to callees only |
| f | Filter by security classification |
| / | Search for function by name |
| e | View edge details (taint flows) |
Call Graph Output Formats
DOT Format (Graphviz)
digraph callgraph {
rankdir=LR;
node [shape=box];
// Sources (green)
"getUserInput" [style=filled, fillcolor="#90EE90"];
// Sinks (red)
"db.query" [style=filled, fillcolor="#FFB6C1"];
// Sanitizers (blue)
"validateId" [style=filled, fillcolor="#ADD8E6"];
// Edges
"handleRequest" -> "getUserInput";
"handleRequest" -> "processUser";
"processUser" -> "validateId";
"processUser" -> "db.query";
}JSON Format
{
"nodes": [
{
"id": "src/api.ts:handleRequest",
"name": "handleRequest",
"file": "src/api.ts",
"line": 15,
"classification": "neutral",
"callers": ["src/server.ts:main"],
"callees": ["src/api.ts:getUserInput", "src/api.ts:processUser"]
},
{
"id": "src/api.ts:getUserInput",
"name": "getUserInput",
"file": "src/api.ts",
"line": 25,
"classification": "source",
"source_type": "http_parameter"
}
],
"edges": [
{
"from": "src/api.ts:handleRequest",
"to": "src/api.ts:getUserInput",
"tainted": false
},
{
"from": "src/api.ts:processUser",
"to": "src/db.ts:query",
"tainted": true,
"taint_source": "src/api.ts:getUserInput"
}
]
}Understanding Edge Details
In the TUI, pressing e on an edge shows detailed security information:
Edge: processUser -> db.query
Caller: processUser (src/services/user.ts:45)
Callee: db.query (src/db/connection.ts:12)
Security Context:
- Tainted: Yes
- Source: req.params.id (src/routes/users.ts:8)
- Sink Type: SQL Query
- Vulnerability: CWE-89 SQL Injection
Data Flow:
1. req.params.id (HTTP Parameter)
2. getUserById(id) (Parameter passing)
3. processUser(user) (Object property)
4. db.query(sql) (SQL concatenation)
Remediation:
Use parameterized queries instead of string concatenation.Filtering the Graph
# Show only security-relevant nodes
rma scan . --call-graph --filter security
# Show only paths from sources to sinks
rma scan . --call-graph --filter vulnerable-paths
# Filter by file
rma scan . --call-graph --filter "src/api/**"
# Filter by function pattern
rma scan . --call-graph --filter "handle*"Use Cases
Security Auditing
Find all paths from user input to database queries:
rma scan . --call-graph --show-paths \
--from "source:http" \
--to "sink:sql"Code Review
Understand the impact of changing a function:
rma scan . --call-graph --callers-of "validateUser" --depth 3Attack Surface Analysis
List all entry points that reach sensitive operations:
rma scan . --call-graph --entry-points-to "sink:*"Cross-File Resolution
Qryon resolves imports across files to build accurate cross-file call graphs:
// src/routes/users.ts
import { UserService } from '../services/userService';
app.get('/user/:id', async (req, res) => {
const user = await UserService.findById(req.params.id);
res.json(user);
});
// src/services/userService.ts
import { Database } from '../db';
export class UserService {
static async findById(id: string) {
return Database.query('users', { id });
}
}Qryon builds a complete graph:
routes/users.ts:handler
-> calls
services/userService.ts:UserService.findById
-> calls
db/index.ts:Database.queryPerformance Considerations
- Large codebases: Use
--max-depthto limit analysis depth - Incremental updates: Qryon caches call graph analysis
- Memory: Large graphs may require more memory
# Limit call graph depth
rma scan . --call-graph --max-depth 5
# Focus on specific directories
rma scan ./src/api --call-graph