A developer workspace for inspecting executables, reviewing source code, and catching issues before they reach production.
RuntimeLens is an open-source static analysis tool built on Next.js. It gives you a single workspace to upload binaries or source files, run automated code review, and inspect executable metadata — without switching between tools.
- Overview
- Features
- Architecture
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- Database
- Usage
- API Reference
- Testing
- Development Workflow
- Known Limitations
- Roadmap
- Contributing
- License
Modern projects can contain hundreds of files. Tracking down security issues, leftover debug code, risky API usage, or understanding an unfamiliar codebase typically means searching manually across editors and terminals.
RuntimeLens consolidates this into one workspace:
- Upload a folder of source files and run a static review across all of them at once
- Upload an executable and inspect its binary format, architecture, sections, and entry point
- Edit source files in the browser and re-run analysis in place
- Organize everything under named projects with persistent storage
- Create and manage named projects with optional descriptions
- Each project stores its own source files and binaries independently
- Dashboard with project listing, binary count, and creation dates
Static analysis runs a set of regex-based rules against source code. Findings are categorized by severity:
| Severity | Meaning |
|---|---|
critical |
Security risk or high-impact issue — eval(), hardcoded secrets, SQL injection patterns, new Function() |
warning |
Code quality or maintainability concern — exec(), innerHTML, dangerouslySetInnerHTML, execSync(), document.write(), FIXME markers, files over 50 KB |
info |
Informational — console.log, TODO markers, unencrypted HTTP URLs, files over 500 lines |
Each finding includes a severity level, title, description, and line number.
Supported file types for review: TypeScript, JavaScript, Python, Java, Go, Rust, C/C++, C#, PHP, Ruby, Swift, Kotlin, SQL, Shell, CSS/SCSS, HTML, JSON, YAML, Markdown.
Upload any file and RuntimeLens will detect:
- Format — PE (Windows
.exe/.dll), ELF (Linux), Mach-O (macOS), or unknown - Architecture —
x86,x86_64,ARM,ARM64 - PE sections — section names parsed directly from the PE header (
.text,.data,.rdata, etc.) - Entry point — address read from the PE optional header
- Source fallback — if the uploaded file is source code rather than a binary, it runs the same static review rules and reports source statistics (total lines, code lines, comment lines, character count)
- Upload individual source files via file picker
- Upload an entire project folder using
webkitdirectory— the full relative path structure is preserved - Files are deduplicated by path on re-upload (upsert behavior)
- Binary files are stored on disk under
storage/binaries/{projectId}/
Source files open in an in-browser editor (plain <textarea> backed by monospace styling). Changes are local to the session and can be re-reviewed without saving to the database.
graph TD
Browser["Browser\n(React 19 / Next.js App Router)"]
subgraph Pages
Home["/ — Landing page"]
Dashboard["/dashboard — Project list"]
NewProject["/projects/new — Create project"]
ProjectPage["/projects/[id] — Project workspace"]
end
subgraph Components
Workspace["ProjectCodeWorkspace\n(file tree · editor · review panel)"]
BinaryUpload["BinaryUpload\n(multipart form upload)"]
BinaryAnalysis["BinaryAnalysis\n(analysis results display)"]
end
subgraph API["Next.js Route Handlers (app/api/)"]
R1["GET /api/projects"]
R2["POST /api/projects"]
R3["GET /api/projects/[id]/files"]
R4["POST /api/projects/[id]/files"]
R5["POST /api/projects/[id]/review"]
R6["POST /api/projects/[id]/binaries"]
R7["POST /api/binaries/[id]/analyze"]
end
subgraph Data
Prisma["Prisma 7\n(better-sqlite3 driver adapter)"]
SQLite["SQLite — dev.db"]
FileSystem["Local filesystem\nstorage/binaries/{projectId}/"]
end
Browser --> Pages
Pages --> Components
Components --> API
API --> Prisma
Prisma --> SQLite
R6 --> FileSystem
R7 --> FileSystem
Upload files (browser FileList)
│
▼
ProjectCodeWorkspace (client component)
│ reads file content via File.text()
▼
POST /api/projects/[id]/review
│ reviewCode() — 17 regex rules
▼
{ findings[], summary: { total, critical, warning, info } }
│
▼
FindingCard list (review panel)
Upload binary (multipart/form-data)
│
▼
POST /api/projects/[id]/binaries
│ saves to storage/binaries/{projectId}/{binaryId}-{name}
│ creates Binary record in SQLite
▼
POST /api/binaries/[id]/analyze
│ detectBinaryFormat() — magic bytes
│ detectArchitecture() — ELF/PE machine field
│ analyzePE() — section table, entry point
│ reviewSource() — if source file, not binary
▼
AnalysisResult { format, architecture, sections, entryPoint, review }
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, Tailwind CSS 4, Lucide React |
| Language | TypeScript 5 (strict mode) |
| ORM | Prisma 7 |
| Database driver | @prisma/adapter-better-sqlite3 |
| Database | SQLite (better-sqlite3) |
| Validation | Zod 4 |
| ID generation | UUID v14 |
| Linting | ESLint 9 (eslint-config-next) |
| Testing | Vitest 4 (installed, no test files yet) |
Note:
@monaco-editor/reactis listed as a dependency but is not used in the current codebase. The editor is a styled<textarea>.
RuntimeLens/
├── app/
│ ├── api/
│ │ ├── projects/
│ │ │ ├── route.ts # GET /api/projects, POST /api/projects
│ │ │ └── [id]/
│ │ │ ├── files/route.ts # GET + POST /api/projects/[id]/files
│ │ │ ├── review/route.ts # POST /api/projects/[id]/review
│ │ │ └── binaries/route.ts # POST /api/projects/[id]/binaries
│ │ └── binaries/
│ │ └── [id]/
│ │ └── analyze/route.ts # POST /api/binaries/[id]/analyze
│ ├── dashboard/page.tsx # Project listing + stats
│ ├── projects/
│ │ ├── new/page.tsx # Create project form
│ │ └── [id]/page.tsx # Project workspace
│ ├── layout.tsx
│ ├── page.tsx # Landing page
│ └── globals.css
├── components/
│ ├── project-code-workspace.tsx # File tree + editor + review panel
│ ├── binary-analysis.tsx # Analysis results UI
│ ├── binary-upload.tsx # Binary upload button
│ └── code-editor.tsx # Standalone textarea editor (unused in workspace)
├── lib/
│ └── prisma.ts # Prisma client singleton (better-sqlite3 adapter)
├── prisma/
│ ├── schema.prisma # Database schema
│ └── dev.db # SQLite database file
├── storage/
│ └── binaries/ # Uploaded binary files (per project)
├── prisma.config.ts # Prisma 7 config (schema path, migrations)
├── package.json
├── tsconfig.json
└── next.config.ts
- Node.js 20 or later
- npm
git clone https://github.com/RahilAlam929/RuntimeLens.git
cd RuntimeLens
npm installCreate a .env file in the project root:
DATABASE_URL="file:./prisma/dev.db"The path is relative to the project root. The prisma.ts client resolves this automatically.
.envfiles are gitignored. Do not commit secrets.
RuntimeLens uses SQLite via the Prisma 7 driver adapter for better-sqlite3. The schema defines three models:
Project
id String (cuid, primary key)
name String
description String?
createdAt DateTime
updatedAt DateTime
binaries Binary[]
files ProjectFile[]
Binary
id String (cuid, primary key)
name String
size Int (bytes)
path String?
projectId String (FK → Project, cascade delete)
createdAt DateTime
ProjectFile
id String (cuid, primary key)
path String (relative path, unique per project)
name String
language String
content String
size Int (bytes)
projectId String (FK → Project, cascade delete)
createdAt DateTime
updatedAt DateTime
── unique constraint on (projectId, path)
── index on projectId
Generate the Prisma client and run migrations:
npx prisma generate
npx prisma migrate dev --name initThe generated client is output to app/generated/prisma/ (gitignored).
npx prisma studionpm run devOpen http://localhost:3000.
- Create a project — Go to
/projects/new, enter a name and optional description. - Upload source files — In the project workspace, use "Upload files" to add individual files or "Upload folder" to add a directory. File content is read in the browser and sent to
/api/projects/[id]/files. - Review a file — Select a file in the tree, click "Review file". Results appear in the right panel.
- Review the entire project — Click "Review project" to scan all reviewable files at once.
- Upload a binary — Click "Upload binary" to attach an executable. RuntimeLens stores it on disk and creates a
Binaryrecord. - Analyze a binary — Click "Run analysis" on any binary card to inspect its format, architecture, sections, and entry point.
All routes are Next.js Route Handlers under app/api/. No authentication is implemented.
Returns all projects ordered by creation date descending.
Response
[
{
"id": "cm...",
"name": "My Project",
"description": "Optional description",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
]Creates a new project.
Request body
{
"name": "My Project",
"description": "Optional description"
}| Field | Type | Required | Constraints |
|---|---|---|---|
name |
string | Yes | 2–100 characters |
description |
string | No | Max 500 characters |
Response — 201 Created
{
"success": true,
"project": { "id": "cm...", "name": "My Project", ... }
}Returns all files for a project, ordered by path ascending.
Response
{
"success": true,
"files": [
{
"id": "cm...",
"path": "src/index.ts",
"name": "index.ts",
"language": "typescript",
"content": "...",
"size": 1234,
"projectId": "cm...",
"createdAt": "...",
"updatedAt": "..."
}
]
}Upserts one or more files into a project. Files are matched by (projectId, path) — existing files are updated, new ones are created.
Request body
{
"files": [
{
"path": "src/index.ts",
"name": "index.ts",
"language": "typescript",
"content": "const x = 1;",
"size": 12
}
]
}Response — 200 OK
{
"success": true,
"count": 1,
"files": [...]
}Runs static analysis on a single file's content. Does not persist the file — analysis is stateless.
Request body
{
"path": "src/auth.ts",
"content": "const password = 'hunter2';"
}Response
{
"success": true,
"projectId": "cm...",
"file": "src/auth.ts",
"findings": [
{
"severity": "critical",
"title": "Possible hardcoded password",
"message": "A password appears to be hardcoded...",
"line": 1
}
],
"summary": {
"total": 1,
"critical": 1,
"warning": 0,
"info": 0
}
}Detection rules (first match per rule wins):
| Rule | Severity | Pattern |
|---|---|---|
| Dynamic code execution | critical | eval( |
| Dynamic function construction | critical | new Function( |
| Hardcoded password | critical | password/passwd/pwd = "..." |
| Hardcoded API key | critical | api_key = "..." |
| Hardcoded secret/token | critical | secret/token = "..." |
| SQL injection | critical | SELECT ... + req/input/params |
| Child process usage | warning | child_process |
| Command execution | warning | exec( |
| Synchronous command execution | warning | execSync( |
document.write |
warning | document.write( |
| Direct HTML injection | warning | innerHTML = |
| Dangerous HTML rendering | warning | dangerouslySetInnerHTML |
FIXME marker |
warning | FIXME |
| Very large file | warning | content > 50 KB |
| Debug logging | info | console.log( |
| Console logging | info | console.debug/info/warn/error( |
| Unencrypted HTTP URL | info | http:// |
TODO marker |
info | TODO |
| Large source file | info | lines > 500 |
Accepts a multipart/form-data upload. The file is stored on disk at storage/binaries/{projectId}/{binaryId}-{sanitized-name} and a Binary record is created in the database.
Request — multipart/form-data
| Field | Type | Constraint |
|---|---|---|
file |
File | Required, max 100 MB |
Response — 201 Created
{
"success": true,
"binary": {
"id": "cm...",
"name": "app.exe",
"size": 204800,
"projectId": "cm...",
"createdAt": "..."
}
}Reads the stored file from disk and performs static analysis.
- If the file has a recognized binary magic header (PE/ELF/Mach-O), it reports binary metadata.
- If the file is source code (detected by extension or content heuristics), it reports source statistics and runs the same review rules as the
/reviewendpoint.
Response
{
"success": true,
"analysis": {
"binary": { "id": "cm...", "name": "app.exe", "size": 204800 },
"type": "binary",
"format": "PE",
"language": null,
"architecture": "x86_64",
"fileSize": 204800,
"sections": 7,
"sectionNames": [".text", ".rdata", ".data", ".pdata", ".rsrc", ".reloc"],
"entryPoint": 4096,
"imports": [],
"exports": [],
"sourceStats": null,
"review": {
"total": 0,
"critical": 0,
"warnings": 0,
"info": 0,
"findings": []
},
"analyzedAt": "2024-01-01T00:00:00.000Z"
}
}For source files, type is "source", sourceStats is populated, format is "Source Code", and review.findings contains the static analysis results (capped at 50 findings).
Vitest is installed but no test files exist in the current codebase.
# Run tests (once test files are added)
npx vitest
# Type checking
npx tsc --noEmit# Install dependencies
npm install
# Set up the database
npx prisma generate
npx prisma migrate dev --name init
# Start the dev server
npm run dev
# Lint
npm run lint
# Type check
npx tsc --noEmit
# Build for production
npm run build
npm run startThese are gaps identified from inspecting the actual codebase:
/api/projects/[id]/review-projectis not implemented. The "Review project" button inProjectCodeWorkspacecalls this endpoint, but noroute.tsexists for it. The button will return a 404.- No authentication or authorization. Any user with access to the server can create, read, and delete data.
- No file size limits on source uploads. The binary upload endpoint enforces a 100 MB cap; the source file upload does not.
- Monaco Editor is not used.
@monaco-editor/reactis listed as a dependency andcode-editor.tsxexists, but the workspace uses a plain<textarea>instead. - Vitest is installed but has no test files.
dev.dbis committed to the repository. The database file atprisma/dev.dbis tracked by git and contains development data.- No
.gitignoreforstorage/. Binary uploads instorage/binaries/are not ignored. - Import/export parsing is not implemented. The analyze endpoint returns empty
imports: []andexports: []arrays. - No rate limiting, CSRF protection, or input sanitization on file paths.
Features clearly absent from the current implementation or noted as future work:
- Implement
POST /api/projects/[id]/review-projectfor multi-file project review - Add test coverage with Vitest
- Add
.gitignoreentry forstorage/anddev.db - Enforce file size limits on source uploads
- Replace
<textarea>with Monaco Editor (dependency already installed)
- Review history — persist findings per file per run
- Finding filtering and search in the review panel
-
.gitignore-aware file upload (skipnode_modules,.next, build directories) - Import/export parsing for PE binaries
- ELF and Mach-O section parsing
- Authentication and project ownership
- GitHub repository import
- CI/CD integration (GitHub Actions, GitLab CI)
- CLI interface
- Additional language-specific analysis rules
- Cross-file analysis (track symbols and call graphs across files)
# Fork the repository, then:
git clone <your-fork-url>
cd RuntimeLens
git checkout -b feature/your-feature
npm install
npx prisma generate
npx prisma migrate dev --name init
npm run devBefore opening a pull request:
npx tsc --noEmit # must pass
npm run lint # must passOpen a pull request against main with a clear description of the change.
No license has been specified for this repository. Contact the author before using this code in other projects.