Skip to content

Repository files navigation

RuntimeLens

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.


Table of Contents


Overview

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

Features

Project Workspace

  • 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

Source Code Review

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.

Binary Analysis

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)

File Upload

  • 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}/

Inline Editor

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.


Architecture

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
Loading

Data Flow — Code Review

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)

Data Flow — Binary Analysis

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 }

Tech Stack

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/react is listed as a dependency but is not used in the current codebase. The editor is a styled <textarea>.


Project Structure

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

Getting Started

Prerequisites

  • Node.js 20 or later
  • npm

Installation

git clone https://github.com/RahilAlam929/RuntimeLens.git
cd RuntimeLens
npm install

Environment Variables

Create 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.

.env files are gitignored. Do not commit secrets.


Database

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

Setup

Generate the Prisma client and run migrations:

npx prisma generate
npx prisma migrate dev --name init

The generated client is output to app/generated/prisma/ (gitignored).

Inspect the database

npx prisma studio

Usage

Start the development server

npm run dev

Open http://localhost:3000.

Workflow

  1. Create a project — Go to /projects/new, enter a name and optional description.
  2. 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.
  3. Review a file — Select a file in the tree, click "Review file". Results appear in the right panel.
  4. Review the entire project — Click "Review project" to scan all reviewable files at once.
  5. Upload a binary — Click "Upload binary" to attach an executable. RuntimeLens stores it on disk and creates a Binary record.
  6. Analyze a binary — Click "Run analysis" on any binary card to inspect its format, architecture, sections, and entry point.

API Reference

All routes are Next.js Route Handlers under app/api/. No authentication is implemented.

Projects

GET /api/projects

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"
  }
]

POST /api/projects

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", ... }
}

Project Files

GET /api/projects/[id]/files

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": "..."
    }
  ]
}

POST /api/projects/[id]/files

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": [...]
}

Code Review

POST /api/projects/[id]/review

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

Binary Upload

POST /api/projects/[id]/binaries

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": "..."
  }
}

Binary Analysis

POST /api/binaries/[id]/analyze

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 /review endpoint.

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).


Testing

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

Development Workflow

# 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 start

Known Limitations

These are gaps identified from inspecting the actual codebase:

  • /api/projects/[id]/review-project is not implemented. The "Review project" button in ProjectCodeWorkspace calls this endpoint, but no route.ts exists 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/react is listed as a dependency and code-editor.tsx exists, but the workspace uses a plain <textarea> instead.
  • Vitest is installed but has no test files.
  • dev.db is committed to the repository. The database file at prisma/dev.db is tracked by git and contains development data.
  • No .gitignore for storage/. Binary uploads in storage/binaries/ are not ignored.
  • Import/export parsing is not implemented. The analyze endpoint returns empty imports: [] and exports: [] arrays.
  • No rate limiting, CSRF protection, or input sanitization on file paths.

Roadmap

Features clearly absent from the current implementation or noted as future work:

Near-term

  • Implement POST /api/projects/[id]/review-project for multi-file project review
  • Add test coverage with Vitest
  • Add .gitignore entry for storage/ and dev.db
  • Enforce file size limits on source uploads
  • Replace <textarea> with Monaco Editor (dependency already installed)

Medium-term

  • Review history — persist findings per file per run
  • Finding filtering and search in the review panel
  • .gitignore-aware file upload (skip node_modules, .next, build directories)
  • Import/export parsing for PE binaries
  • ELF and Mach-O section parsing

Longer-term

  • 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)

Contributing

# 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 dev

Before opening a pull request:

npx tsc --noEmit   # must pass
npm run lint       # must pass

Open a pull request against main with a clear description of the change.


License

No license has been specified for this repository. Contact the author before using this code in other projects.

About

A developer workspace for inspecting executables, reviewing source code, and catching issues before they reach production.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages