Skip to content

chore: public-readiness P0 — CORS/WS lockdown, NTC rename, action pinning, badge casing#6

Open
navidrast wants to merge 6 commits intomainfrom
chore/public-readiness-p0
Open

chore: public-readiness P0 — CORS/WS lockdown, NTC rename, action pinning, badge casing#6
navidrast wants to merge 6 commits intomainfrom
chore/public-readiness-p0

Conversation

@navidrast
Copy link
Copy Markdown
Collaborator

@navidrast navidrast commented Apr 20, 2026

Summary

Batch of four small, independent changes from the public-readiness audit (P0 items from the enterprise-architect review). All four are what I'd want to land before flipping the repo public — they either close a security footgun, fix a cosmetic "weekend project" signal, or shore up supply-chain hygiene.

Commits (reviewable top-down, each stands alone)

  1. chore(ci): pin trivy-action and tim-actions/dco to tagged releases — eliminates the @master supply-chain risk. trivy-action → v0.35.0, dco → SHA-pinned to master (see fix commit e72a5dd, v1.1.0 was incompatible).
  2. refactor(app): rename leftover NTC identifiers to OpenDrayNtcAppOpendrayApp, ntcTerminalThemeopendrayTerminalTheme, __ntcFontInstalled__opendrayFontInstalled, and pubspec.yaml description swapped from the default "A new Flutter project." to the real product tagline. No behavioural change.
  3. docs(readme): normalize org casing to Opendray/opendray — badges, clone URL, discussions link now all use the canonical org slug. Go module path (github.com/opendray/opendray) is untouched.
  4. feat(security): configurable CORS + WebSocket origin allowlist — the substantive one. Replaces wide-open Access-Control-Allow-Origin: * and CheckOrigin: return true with a single originPolicy driven by ALLOWED_ORIGINS env var. Safe default is same-origin only; * still available but logged loudly; comma-separated allowlist supported. Five unit tests in gateway/origins_test.go cover matching, wildcard, same-origin fallback, OPTIONS preflight, and the browser-enforces-not-server contract.
  5. fix(ci): pin tim-actions/dco to master SHA instead of incompatible v1.1.0 — follow-up fix; v1.1.0 broke the DCO workflow because its input schema diverged from master's.

Behaviour change & migration

Only #4 has externally observable behaviour:

  • Mobile apps (iOS/Android): no change. Native clients never send Origin; they pass every code path.
  • Flutter web served by this binary: no change. Same-origin always works.
  • make dev (Flutter devserver on :5000 → Go on :8640): now needs ALLOWED_ORIGINS=http://localhost:5000. Documented in .env.example.
  • Production behind an authenticating reverse proxy: no change if the proxy sets Origin to match the user-facing host; otherwise set ALLOWED_ORIGINS=https://your.public.host.
  • Deployments that relied on *: set ALLOWED_ORIGINS=* to keep existing behaviour (server logs a warning at startup).

Known failing checks (pre-existing on main, NOT caused by this PR)

Two checks will continue to fail on this PR regardless of changes because they're broken on main:

  • Backend (Go)TestRunner_EchoSucceeds: flaky test with a race in gateway/tasks/runner_test.go output-drain logic. Same failure appears on the feat: enterprise hardening commit on main. Worth a separate good first issue — the fix is ~10 lines.
  • Analyze Go (CodeQL): has failed on every recent CI run across all branches (5/5 recent). The autobuild step hits Running /usr/bin/make failed, continuing anyway; something downstream produces "configuration error" with zero diagnostics. Needs separate investigation.

Verified by checking gh run list history — both were red before this PR was opened. Happy to open follow-up issues for both if you want.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • go test -race -count=1 ./gateway/... clean (new origin-policy tests pass; TestRunner_EchoSucceeds passes 3/3 locally — it's a CI-specific race)
  • Manual: make dev with ALLOWED_ORIGINS=http://localhost:5000 — Flutter devserver can still reach the Go backend
  • Manual: browse the deployed Flutter web build end-to-end, confirm WS reconnects and panels load (same-origin, no env needed)
  • Manual: a cross-origin curl -H "Origin: https://evil.example" against a running instance should receive no Access-Control-Allow-Origin header

Not in this PR (deferred)

  • P0 Welcome to OpenDray + Roadmap #1 — README screenshot/GIF. Needs asset capture first; separate PR.
  • P1 — test coverage for kernel/store, kernel/terminal, kernel/auth, cmd/opendray.
  • P1 — docs/plugin-authoring.md, docs/architecture.md, docs/deployment.md.
  • P1 — OpenAPI spec, .golangci.yml, .editorconfig.

Merge strategy recommendation

Rebase and merge — each commit has a distinct conventional-commit prefix that release-please parses into CHANGELOG entries. Squashing would flatten five meaningful entries into one.

🤖 Generated with Claude Code

RCC Bot and others added 6 commits April 21, 2026 04:29
Replace `@master` references with immutable tags to eliminate the
supply-chain risk of an upstream branch getting rewritten between runs.

- aquasecurity/trivy-action: master → v0.35.0
- tim-actions/dco: master → v1.1.0

Both were flagged by the public-readiness audit as the only unpinned
third-party actions in the workflow tree.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: RCC Bot <rcc@localhost>
The Flutter app still carried identifiers from the project's earlier name
(NTC). These are the last rename leftovers — a first-time public visitor
sees the root widget class, the package description, and terminal theme
on `grep` within seconds, and an inconsistent name reads as abandoned.

Renamed:
- NtcApp / _NtcAppState → OpendrayApp / _OpendrayAppState (app.dart, main.dart)
- ntcTerminalTheme → opendrayTerminalTheme (terminal_theme.dart + usage in session_page.dart)
- window.__ntcFontInstalled → window.__opendrayFontInstalled (preview_page.dart JS interop)

Also replaced the placeholder pubspec description ("A new Flutter project.")
with the actual product tagline.

No behavioural change. Purely cosmetic.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: RCC Bot <rcc@localhost>
GitHub redirects lower-case org slugs to the canonical form, but the
README had a mix of `opendray/opendray` (badges, clone URL, discussions
link) and `Opendray/opendray` (install script, releases links). Badges
in particular render more reliably when they hit the canonical slug on
the first request.

Purely a docs/url change — no Go import paths touched (the module path
`github.com/opendray/opendray` is intentionally lowercase and stays so).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: RCC Bot <rcc@localhost>
Both `corsMiddleware` (in gateway/server.go) and the package-level
gorilla/websocket `upgrader` (gateway/ws.go) were wide open — `Access-
Control-Allow-Origin: *` unconditionally, and `CheckOrigin` always
returning true. For a self-hosted tool whose PTY endpoints are root-
equivalent, the safe default is the other way round: block cross-origin
by default, opt in explicitly.

The fix introduces a single `originPolicy` (gateway/origins.go) that
drives both CORS header stamping and WebSocket origin validation off
the same allowlist. Populated from the new `ALLOWED_ORIGINS` env var
(comma-separated) in cmd/opendray/main.go.

Behaviour:
  - empty          → same-origin only. No Allow-Origin header stamped;
                     WS CheckOrigin falls back to Origin.host == Host.
                     Native mobile clients (no Origin header) always
                     pass, unchanged.
  - "*"            → wildcard. Logged loudly at startup so operators
                     see they've opted out.
  - a,b,c          → exact-match allowlist. Allow-Origin echoes the
                     matched value; WS CheckOrigin accepts the entry.

The four WS call sites (ws.go, logs.go, tasks_handlers.go,
simulator_stream.go) now share the Server-scoped upgrader so policy is
consistently enforced across every PTY, tail, task, and simulator
stream.

Setup mode (gateway.NewSetup) uses its own empty policy — the wizard is
served from the same binary and never speaks cross-origin.

Tests: gateway/origins_test.go covers allowlist matching (including
trailing-slash normalisation and blank-entry tolerance), wildcard
behaviour, same-origin fallback for WS, OPTIONS preflight short-
circuit, and the "browser, not server, enforces CORS" contract —
disallowed origins still reach the handler, they just miss the
Allow-Origin header and get blocked in the browser.

README.md documents `ALLOWED_ORIGINS` in the Key variables table;
.env.example explains the three forms with concrete examples,
including the `make dev` snippet.

Pre-existing kernel/config test failures in this environment are
unrelated (host shell has DB_HOST / LISTEN_ADDR exported from an
adjacent sandbox) — they also fail on main without these changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: RCC Bot <rcc@localhost>
….1.0

v1.1.0 of the action ships a breaking input-schema change — it requires
`commits:` and no longer accepts `token:`, which is what this workflow
was written against. The previous `@master` reference happened to work
because master is still on the older API.

Pin to the current master SHA (f2279e6) so we get reproducible,
supply-chain-hardened behaviour without the tag-swap surprise. If and
when the maintainer cuts a v1.2.x that re-adds `token:` we can
migrate.

Introduced in c0ff78f.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: RCC Bot <rcc@localhost>
Adds three organised badge rows plus a live star-history chart so
visitors landing on the repo see a real-time picture of project
health, community traction, and adoption.

Rows:
  - Build & release: CI, CodeQL, Coverage, Release, License
  - Community:       Stars, Forks, Watchers (social-style for prominence)
  - Project health:  Downloads, Contributors, Last commit, Open issues,
                     Open PRs, Discussions

The Star History section uses star-history.com's SVG embed with a
`<picture>` element so it respects the viewer's light/dark-mode
preference. All badge images are served via shields.io / star-history —
no tracking pixels, no third-party JS, safe for GitHub's markdown
sandbox.

Pure README change. No code, no behaviour, no dependencies touched.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: RCC Bot <rcc@localhost>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant