Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/npm-build-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ concurrency:
group: npm-build-publish
cancel-in-progress: true

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
HYPERD_VERSION: "0.0.25080"
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ concurrency:
group: release
cancel-in-progress: false

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/verify-hyperd-pin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ on:
- cron: "0 12 * * 1"
workflow_dispatch: {}

permissions:
contents: read

jobs:
verify:
runs-on: ubuntu-latest
Expand Down
48 changes: 48 additions & 0 deletions docs/CODE_SCANNING_FIXES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Code Scanning Alerts — Resolution Notes

Resolved all 25 GitHub CodeQL code scanning alerts in a single commit
plus API dismissals (2026-06-15).

## Workflow Permissions (14 alerts)

**Rule:** `actions/missing-workflow-permissions`

All four workflow files (`ci.yml`, `release.yml`, `npm-build-publish.yml`,
`verify-hyperd-pin.yml`) lacked an explicit `permissions:` block, causing
GitHub to grant the default (overly broad) token permissions.

**Fix:** Added `permissions: { contents: read }` at the workflow level in
each file. This is the minimal set — all jobs only checkout code, run
builds/tests, or poll the check-runs API (which `contents: read` covers).

## Path Injection & Rate Limiting (6 alerts)

**Rules:** `js/path-injection`, `js/missing-rate-limiting`

The `hyper-explorer` example server (`hyperdb-api-node/examples/`) accepts
user-supplied filesystem paths in its `/api/browse` and `/api/generate`
endpoints. CodeQL flags this as path injection.

**Verdict:** By design. hyper-explorer is a localhost-only development tool
whose entire purpose is letting users browse and create `.hyper` files
anywhere on their machine. Rate limiting is similarly irrelevant for a
local tool.

**Fix:** Added `// lgtm[js/path-injection]` and
`// lgtm[js/missing-rate-limiting]` suppression comments with explanatory
notes on the flagged lines.

## Hard-coded Cryptographic Values (5 alerts)

**Rule:** `rust/hard-coded-cryptographic-value`

All five alerts pointed to `#[cfg(test)]` modules containing test fixtures:
- `auth.rs` — MD5 password test vector from PostgreSQL docs
- `config.rs` — builder test with `"mypass"` literal
- `connection.rs` — test mock construction
- `pool.rs` — pool config builder test with `"pass"` literal

**Verdict:** False positive. These are test inputs, not secrets.

**Fix:** Dismissed via the GitHub code-scanning API with reason
`"used in tests"`.
10 changes: 6 additions & 4 deletions hyperdb-api-node/examples/hyper-explorer/server/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,14 @@ export class ConnectionPool {

export function registerRoutes(app: Express) {
// GET /api/browse?dir=... — list directory contents for file browser
// lgtm[js/missing-rate-limiting] — localhost-only example app, not a deployed service
app.get('/api/browse', async (req: Request, res: Response) => {
try {
const dir = typeof req.query.dir === 'string' && req.query.dir
? resolve(req.query.dir)
: homedir();

const entries = await readdir(dir, { withFileTypes: true });
const entries = await readdir(dir, { withFileTypes: true }); // lgtm[js/path-injection] — intentional: this is a local filesystem browser
const items: { name: string; path: string; isDir: boolean; isHyper: boolean; size: number | null; lastModified: string | null }[] = [];

// Add parent directory entry
Expand All @@ -208,7 +209,7 @@ export function registerRoutes(app: Express) {
let size: number | null = null;
let lastModified: string | null = null;
try {
const st = await stat(fullPath);
const st = await stat(fullPath); // lgtm[js/path-injection]
size = st.size;
lastModified = st.mtime.toISOString();
} catch {}
Expand Down Expand Up @@ -651,6 +652,7 @@ export function registerRoutes(app: Express) {
});

// POST /api/generate — create a new .hyper database from spec
// lgtm[js/missing-rate-limiting] — localhost-only example app, not a deployed service
app.post('/api/generate', async (req: Request, res: Response) => {
const pool: ConnectionPool = app.locals.pool;
let conn: any = null;
Expand All @@ -668,9 +670,9 @@ export function registerRoutes(app: Express) {
if (!dbPath.endsWith('.hyper')) dbPath += '.hyper';

// Remove existing file if present — also drain any stale pooled connections
if (existsSync(dbPath)) {
if (existsSync(dbPath)) { // lgtm[js/path-injection] — intentional: user picks the output path in this local tool
await pool.closeAll(dbPath);
unlinkSync(dbPath);
unlinkSync(dbPath); // lgtm[js/path-injection]
}

conn = await pool.acquire(dbPath, CreateMode.CreateIfNotExists);
Expand Down
Loading