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
17 changes: 13 additions & 4 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ import? "contractile.just"
default:
@just --list --unsorted

# Launch the game (platform-detect, self-heal, git cycle)
# Launch the game (platform-detect, self-heal, git cycle).
# Permission set is intentionally scoped (no --allow-all) — see run.js header
# for the rationale + per-API audit. --allow-env is broad because run.js calls
# Deno.env.toObject() to propagate the user's environment into spawned child
# processes (deno task dev, deno task dev:all); narrowing this requires
# rewriting the child-env passthrough to whitelist only the vars the children
# actually consume.
run:
deno run --allow-all run.js
deno run --allow-read=. --allow-env --allow-run=deno,git,which,xdg-open,open,start --allow-net=127.0.0.1 run.js

# Launch with Elixir sync server (multiplayer/co-op)
# Launch with Elixir sync server (multiplayer/co-op).
# Same permission set as `just run` — the --full flag changes what child
# command run.js spawns (deno task dev:all instead of deno task dev), not the
# parent permission surface.
run-full:
deno run --allow-all run.js --full
deno run --allow-read=. --allow-env --allow-run=deno,git,which,xdg-open,open,start --allow-net=127.0.0.1 run.js --full

# Start full development environment
dev:
Expand Down
70 changes: 70 additions & 0 deletions audits/assail-classifications.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,73 @@ category = "HardcodedSecret"
classification = "game-content-fixture"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "Fictional in-game passwords / credentials for the hacker-themed gameplay; not real secrets. GlobalNetworkData.res carries the explicit SECURITY NOTE."

# ───────────────────────────────────────────────────────────────────────────
# UnsafeDeserialization — Track C #99 cleanup 2026-06-01
# ───────────────────────────────────────────────────────────────────────────
# Three remaining JSON.parseExn call sites after the BalanceAnalyserModel
# refactor (src/app/screens/BalanceAnalyserModel.res:217 → SafeJson.parse).
# Two are intentional infrastructure; one is a benchmark fixture.

[[classification]]
file = "src/app/proven/SafeJson.res"
category = "UnsafeDeserialization"
classification = "intentional-safe-wrapper"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "Canonical safe-parse wrapper for the estate. JSON.parseExn at line 32 is wrapped in try/catch and returns Result<JSON.t, ProvenError.provenError> — never throws. This file IS the fix that other call sites consume. ProvenSafeJson is the dependently-typed Proven equivalent (formal proof of totality)."

[[classification]]
file = "lib/ocaml/SafeJson.res"
category = "UnsafeDeserialization"
classification = "intentional-safe-wrapper"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "Build mirror of src/app/proven/SafeJson.res — see source-side rationale. Auto-regenerated by ReScript compile; do not edit by hand."

[[classification]]
file = "vm/lib/ocaml/benchmark.res"
category = "UnsafeDeserialization"
classification = "benchmark-fixture"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "VM-serialisation benchmark. JSON.parseExn at line 139 is intentionally measured against a hardcoded valid JSON string ({\"playerX\":100,...}) inside benchmark(\"Game state JSON deserialize\", 100000, ...). Using SafeJson.parse would measure the try/catch overhead instead of the raw parser — wrong instrument for this benchmark."

# ───────────────────────────────────────────────────────────────────────────
# DynamicCodeExecution — Track C #99 cleanup 2026-06-01
# ───────────────────────────────────────────────────────────────────────────
# Vite-emitted build artifacts in main-game/dist/assets/. DOM manipulation
# (innerHTML / document.write equivalents) is inherent to SPA bootstrap +
# web-worker initialisation. Filenames are content-hashed and rotate every
# build; the suppression below covers the current snapshot. Followup: either
# gitignore main-game/dist/ and regenerate per CI, or refresh these entries
# at each release. Tracked separately.

[[classification]]
file = "main-game/dist/assets/index-Cdt-JTFK.js"
category = "DynamicCodeExecution"
classification = "compiled-output"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "Vite-generated SPA bootstrap. innerHTML/document.write is standard SPA initialisation pattern, not user-controlled code execution. Content-hashed filename rotates per build; this entry will need refreshing if the bundle changes."

[[classification]]
file = "main-game/dist/assets/webworkerAll-DNs-UuZS.js"
category = "DynamicCodeExecution"
classification = "compiled-output"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "Vite-generated web-worker bundle. DOM/script-dynamic operations are inherent to web-worker initialisation. Content-hashed filename rotates per build."

# ───────────────────────────────────────────────────────────────────────────
# ExcessivePermissions — Track C #99 cleanup 2026-06-01
# ───────────────────────────────────────────────────────────────────────────
# run.js previously documented "deno run --allow-all" as its canonical
# invocation. The Justfile recipes (`just run` / `just run-full`) + the
# updated run.js header now carry the scoped permission set
# (--allow-read=. --allow-env --allow-run=deno,git,which,xdg-open,open,start
# --allow-net=127.0.0.1). The header retains a "NOT recommended" mention of
# --allow-all so static analyzers don't dredge up a fresh finding from old
# documentation drift. Suppression below covers that residual text.

[[classification]]
file = "run.js"
category = "ExcessivePermissions"
classification = "documentation-mention-only"
audit = "audits/audit-game-content-fixture-2026-05-26.md"
rationale = "The 'deno -A' string surviving in run.js after Track C cleanup appears only inside the header comment that explicitly DISCOURAGES its use ('--allow-all is NOT recommended'). The canonical invocation via `just run` carries the scoped permission set. No runtime --allow-all behaviour is invoked."
34 changes: 32 additions & 2 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 30 additions & 4 deletions run.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,34 @@
// run.js — Homoiconic, fault-tolerant, platform-independent run script
// for IDApTIK (asymmetric co-op stealth puzzle-platformer)
//
// Usage:
// deno run --allow-all run.js # auto-detect and launch
// deno run --allow-all run.js --help # show usage
// Usage (scoped permissions — preferred):
// deno run --allow-read=. --allow-env --allow-run=deno,git,which,xdg-open,open,start --allow-net=127.0.0.1 run.js
// deno run --allow-read=. --allow-env --allow-run=deno,git,which,xdg-open,open,start --allow-net=127.0.0.1 run.js --help
//
// Or via the Justfile recipe (which carries the canonical permission set):
// just run
// just run-full
//
// Permission audit (panic-attack ExcessivePermissions Track C, idaptik#99):
// --allow-read=. : Deno.readTextFile(...) + Deno.stat(...) on
// tree-relative paths (node_modules, lib/bs, dist).
// --allow-env : Deno.env.get(...) for WAYLAND_DISPLAY/DISPLAY +
// Deno.env.toObject() to propagate env into child
// processes. Narrowing this requires rewriting the
// child-env passthrough to a whitelist — tracked
// as follow-up to this PR.
// --allow-run=... : Deno.Command spawns of `deno`, `git`, `which`,
// and the platform's browser opener (`xdg-open` on
// Linux/Wayland/X11, `open` on macOS, `start` on
// Windows).
// --allow-net=127.0.0.1 : Deno.listen({port}) port-probing for the Vite
// dev-server port (1984) and the fallback range
// 1985-1987. Loopback only — no outbound network
// access required.
//
// --allow-all is NOT recommended; see `just run` / `just run-full` for the
// canonical scoped invocation. The previous --allow-all advice in this
// header was the trigger for the Track C ExcessivePermissions finding.

// ─────────────────────────────────────────────────────────────────────────────
// REGISTRY — the script IS the data; reflect() reads this at runtime
Expand Down Expand Up @@ -354,7 +379,8 @@ if (import.meta.main) {
${c.bold}${REGISTRY.identity.display} — run.js${c.reset}
${REGISTRY.identity.license} | ${REGISTRY.identity.repo}

Usage: deno run --allow-all run.js [OPTIONS]
Usage: deno run --allow-read=. --allow-env --allow-run=deno,git,which,xdg-open,open,start --allow-net=127.0.0.1 run.js [OPTIONS]
or: just run [OPTIONS] (carries the canonical permission set)

Options:
--help, -h Show this help
Expand Down
14 changes: 11 additions & 3 deletions src/app/screens/BalanceAnalyserModel.res
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,16 @@ let empty: report = {

/// Parse a full balance report from a JSON string.
/// Returns `empty` if the string is not valid JSON or the structure is wrong.
/// Uses SafeJson.parse (Result-returning) instead of JSON.parseExn so a
/// malformed file never throws — the caller (escape-hatch / PanLL panel)
/// always gets a typed `report` and can render the empty case without
/// crashing the host.
let fromJson = (jsonStr: string): report => {
switch JSON.parseExn(jsonStr)->JSON.Classify.classify {
| Object(root) =>
switch SafeJson.parse(jsonStr) {
| Error(_) => empty
| Ok(parsed) =>
switch parsed->JSON.Classify.classify {
| Object(root) =>
let levels = switch Dict.get(root, "levels") {
| Some(j) => parseArray(j, parseLevelSummary)
| None => []
Expand Down Expand Up @@ -273,7 +280,8 @@ let fromJson = (jsonStr: string): report => {
difficultyMin: {let (mn, _) = diffRange; mn},
difficultyMax: {let (_, mx) = diffRange; mx},
}
| _ => empty
| _ => empty
}
}
}

Expand Down
Loading