Safebox · Static Analysis
Upload a codebase in PHP, JavaScript, or Python. Get a capability graph, data-flow report, and security audit. Every finding references the original source file and line number. The developer never reads U.
The problem
PHPStan analyzes PHP. ESLint analyzes JavaScript. Mypy analyzes Python. None of them can analyze a codebase that uses all three. And most real systems do. The backend is PHP. The frontend is JavaScript. The ML pipeline is Python. The security properties that matter — what data flows where, what each module can actually do, whether access checks are present on every path — span all three.
Running three separate analysis tools on three separate codebases gives you three separate reports with three separate type systems and no shared understanding of how the pieces connect.
The analysis engine solves this by transpiling everything to U first.
PHP, JavaScript, TypeScript, Python — each has a transpiler that produces U code plus a V3 source map. The analysis runs on U, the common representation. Findings map back through source maps to the original language, file, and line. One codebase. One analysis. One report.
Architecture
The pipeline is deterministic. Same input, same output. The analysis is reproducible by anyone with the same transpiler and analysis versions. This is what makes it suitable for regulatory compliance and M-of-N attestation.
01 · Analysis pass
Every U function declares what it can do through modifiers. The analyzer walks the call graph and verifies that every function only uses capabilities it declared — or that its callers declared and passed down.
A function that calls filesystem.write without +IO in its signature chain is a violation. A module that imports a network library without +Net anywhere in its dependency tree is a violation. Caught at analysis time, not runtime.
| Modifier | Meaning | Grants access to |
|---|---|---|
+IO | Filesystem | Read, write, delete files |
+Net | Network | HTTP, TCP, DNS, sockets |
+DB | Database | PDO, query execution |
+Crypto | Cryptography | Encrypt, decrypt, sign, hash |
+Exec | Subprocess | Shell commands, process spawn |
+Unsafe | Raw memory | FFI, pointer arithmetic, C interop |
19 PHP files, 15,057 lines. Transpiled to 7,428 lines of U. The analyzer produces:
| Module | Capabilities | Evidence |
|---|---|---|
| Db_Sqlite | +DB, +FS | Sqlite.php:52 — new Database(filePath) |
| Db_Mysql | +DB, +Net | Mysql.php:99 — mysql2.createConnection(o) |
| Db_Postgres | +DB, +Net | Postgres.php:47 — new pg.Client(connString) |
| Db_Row | +Event | Row.php:619 — Q::event("Db/Row/…/save") |
| Db_Query | none (pure) | Builds SQL strings, no side effects |
| Db_Expression | none (pure) | String concatenation only |
| Db_Utils | +FS, +Exec | Utils.php:829 — heredoc templates written to files |
Every capability is grounded in a source line. An auditor clicks through to the exact PHP code that causes the classification.
02 · Analysis pass
When a value flows from user input through processing to a database query, the analyzer traces the path and flags missing sanitization. This works across language boundaries because PHP $_GET, JS req.query, and Python request.args all become the same U pattern: a read from an untrusted input source.
| Finding | Source | Sink | Missing |
|---|---|---|---|
| SQL injection risk | Request.php:45 | Query.php:1595 | No parameterized binding |
| XSS risk | Request.php:112 | Response.php:89 | No html_encode |
| Path traversal | Upload.php:33 | Sqlite.php:52 | No path validation |
One set of taint-tracking rules, applied uniformly across PHP, JS, and Python. Not three separate tools with three separate rule sets.
03 · Analysis pass
The Qbix Platform has parallel PHP and JS implementations of the same Db classes. The analyzer can verify that both versions have compatible type signatures:
PHP: Db_Row::save(array $options): bool
JS: Db.Row.prototype.save = function(options, callback)
Finding: return type mismatch — PHP returns bool synchronously, JS uses callback pattern. Parameter: PHP typed array vs JS plain object. Acceptable for async/sync split.
ESLint doesn't know about PHP types. PHPStan doesn't know about JS callbacks. The U representation makes both visible in the same analysis.
04 · Analysis pass
Every require, include, and import in every language becomes a U o (import) statement. The analyzer builds the full dependency graph with capabilities propagated:
When a dependency updates and gains a new capability — a library that only did computation now makes network calls — the diff is immediate: "version 2.4.0 added +Net, +Exec not present in 2.3.1." The new capabilities are traced to specific lines in the updated library.
05 · Analysis pass
The Qbix Platform uses access levels — testReadLevel, testWriteLevel, testAdminLevel — throughout its Streams system. The analyzer verifies that every data path checks access before returning data:
| Endpoint | Access check | Status |
|---|---|---|
Streams_stream_post | testWriteLevel('edit') at line 34 | ✓ Checked |
Streams_stream_get | testReadLevel('content') at line 22 | ✓ Checked |
Streams_message_post | None found before $stream->post() | ✗ Missing |
Static analysis of the access control pattern. Catches the common bug where a developer adds a new endpoint and forgets the access check.
06–08 · Additional passes
The call graph shows which functions are reachable from entry points. Functions never called, classes never instantiated, imports never used:
Db/Utils.php: 12 of 47 functions unreachable from any entry point — compare_dbRows (line 28), generateModels (line 823), …
U's fiber model makes concurrency explicit. The analyzer detects two fibers accessing the same mutable state without synchronization:
Row.php:205 — $fieldsModified accessed by both save() and retrieve() fibers. No lock.
Algorithmic complexity is a structural property of U code. Nested loops, repeated allocations, unnecessary copies:
Query.php:866 — _criteria_expression: O(n²) nested iteration over criteria keys and parameters.
Applications
Submit a codebase. Get a capability inventory, data-flow analysis, and access-control verification — in minutes, not weeks. The auditor reviews findings grounded in source lines, not raw code.
Run on every pull request. If a change adds a new capability — a module that didn't do network IO now imports curl — the CI build flags it: "This PR adds +Net to PaymentProcessor.php — was this intentional?"
Financial services, healthcare, government. The capability report is a machine-generated, reproducible, deterministic artifact. Auditors verify by re-running — same input, same output.
Diff capability graphs across dependency versions. Flag libraries that gained +Net or +Exec between releases. Trace the new capabilities to specific lines.
Migrating from PHP to TypeScript? Both versions transpile to U. Compare the U representations: same signatures, same capabilities, same data flow. Discrepancies reported with source lines in both languages.
Before code runs inside a Safebox, analysis verifies that capability declarations match actual behavior. A module that declares [+DB] but uses [+DB, +Net] is rejected. M-of-N signers attest the analysis passed.
Students submit code. The analyzer shows what their code actually does — not what they think it does. "Your function declares it only reads from the database, but it also writes to the filesystem at line 47 through this call chain…"
Current state
| Component | Status | Tested |
|---|---|---|
| PHP→U transpiler | ✓ Production + source maps | 19/19 Qbix Db files |
| JS→U transpiler | ✓ Production + source maps | tree-sitter + acorn |
| Python→U transpiler | ✓ Production + source maps | tree-sitter based |
| Source map module | ✓ V3/VLQ, browser + Node | 132 lines |
| Zend engine bridge | ✓ PHP 8.3.6 embed | 24/24 tests |
| N-API bridge | ✓ Node v22 native addon | 36/36 tests |
| Event loop bridge | ✓ libuv ↔ U fibers | 10/10 bidirectional |
| Db ORM (PHP, Zend) | ✓ SQLite + MySQL + PostgreSQL | 40/40 tests |
| Db ORM (JS, Node) | ✓ SQLite + MySQL + PostgreSQL | 24/24 tests |
| Bootstrap chain | ✓ JS→U→u2c→C→binary | 3,080 → 1,997 lines |
| Component | Status | Effort |
|---|---|---|
| Capability annotation system | Not yet | Define +IO/+Net/+FS/+DB/+Exec in U spec |
| Capability analysis pass | Not yet | Walk call graph, check declarations |
| Data-flow taint tracking | Not yet | Source/sink rules for U patterns |
| Cross-language type diff | Not yet | Compare PHP and JS U representations |
| M-of-N signing | Not yet | Header slots in .u module format |
| Site playground | Not yet | Two-pane editor, source-map navigation |
| Zip upload + batch analysis | Not yet | Frontend + backend for multi-file projects |
The analyzer is a pipeline, not a monolith. Each pass is independent. The transpilers are deterministic. The source maps are standard V3. Every finding references original source. Same input, same output, every time. — The simple version