Skip to content

Commit 9010d2a

Browse files
authored
Merge pull request #36 from devswha/codex/adversarial-skills-e2e
fix: harden execution boundaries and preserve async client state
2 parents ddaeb63 + 905f4c5 commit 9010d2a

70 files changed

Lines changed: 3403 additions & 369 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@ VITE_PORT=5173
1818
# access is not required.
1919
HOST=0.0.0.0
2020

21-
# Vite host admission
22-
# Vite accepts localhost and IP addresses without this setting. To use a DNS
23-
# name, list it here with commas between names; a leading dot matches its
24-
# subdomains.
21+
# HTTP/WebSocket and Vite host admission
22+
# Localhost and literal IP addresses work without this setting. Other DNS
23+
# names require an explicit list, including HTTPS reverse proxies and tailnet
24+
# names. Set this in the app server's environment before upgrading an existing
25+
# DNS-based deployment. Use commas between names; no schemes, paths, or ports.
26+
# A leading dot admits that domain and its subdomains.
2527
#
2628
# ALLOWED_HOSTS=macbookpro.tail1e211e.ts.net
2729
# ALLOWED_HOSTS=.tail1e211e.ts.net
2830
# ALLOWED_HOSTS=gjc.example.com,.internal
2931
#
30-
# ALLOWED_HOSTS=* turns off this protection. Do that only on a trusted network:
31-
# the UI can execute commands as the server user.
32+
# ALLOWED_HOSTS=* disables the DNS-rebinding host protection. Prefer explicit
33+
# names: a hostile page can otherwise target the server through its own DNS
34+
# name, even when the server binds loopback. See docs/SELF-HOST.md for migration.
3235

3336
# Optional Claude executable override.
3437
# CLAUDE_CLI_PATH=claude

docs/SELF-HOST.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,62 @@ port forwarding.
6161
ssh -N -L 3001:127.0.0.1:3001 user@server
6262
```
6363

64+
## DNS host admission and reverse-proxy migration
65+
66+
The server now rejects unlisted DNS names before serving HTTP routes or
67+
accepting WebSocket upgrades. With `ALLOWED_HOSTS` unset, `localhost` and
68+
literal IPv4/IPv6 addresses continue to work. Matching `Host` and `Origin`
69+
headers, an omitted `Origin`, or `X-Forwarded-Proto: https` do not admit a DNS
70+
name. This prevents an attacker-controlled DNS name from reaching a loopback
71+
server after rebinding.
72+
73+
**Compatibility change:** older releases accepted arbitrary matching DNS
74+
Host/Origin pairs. Existing HTTPS reverse proxies, custom local DNS aliases
75+
and tailnet DNS names must now be listed in the **app server's environment**.
76+
Changing only Nginx's `server_name` is insufficient. Direct loopback/IP access
77+
and SSH tunnels using `localhost` need no migration.
78+
79+
Before upgrading a DNS-based deployment, create a service drop-in:
80+
81+
```sh
82+
systemctl --user edit gajae-app.service
83+
```
84+
85+
Add a setting such as:
86+
87+
```ini
88+
[Service]
89+
Environment="ALLOWED_HOSTS=gjc.example.com,macbook.tailnet.example"
90+
```
91+
92+
Replace these examples with names you control. Entries are comma-separated
93+
hostnames without schemes, ports or paths. A leading dot such as
94+
`.internal.example` admits that domain and all its subdomains; use an exact
95+
name when only one host should be trusted. Source-development deployments
96+
can put the same setting in `.env`; both Vite and the API server use it.
97+
98+
Keep the proxy forwarding the public Host (`proxy_set_header Host $host;` in
99+
the [Nginx example](nginx-subpath-template.conf)). A proxy that uses a DNS
100+
upstream name as Host must also list that name, or forward a loopback/IP Host;
101+
the public browser origin still needs its own entry. HTTPS remains supported
102+
with this explicit configuration. Forwarded headers do not replace admission.
103+
104+
After the upgrade, reload the service configuration and restart the app:
105+
106+
```sh
107+
systemctl --user daemon-reload
108+
systemctl --user restart gajae-app.service
109+
curl --fail http://127.0.0.1:3001/health
110+
```
111+
112+
Check the published page and its WebSocket connection. HTTP requests with an
113+
unlisted Host return 403; WebSocket upgrades are rejected with 401. Configure
114+
health checks using a permitted Host.
115+
116+
`ALLOWED_HOSTS=*` retains an explicit compatibility escape hatch, but disables
117+
this DNS host protection. Host admission does not authenticate remote clients;
118+
keep the existing loopback/VPN/tunnel or authenticated-proxy access boundary.
119+
64120
## Cutover to a verified release
65121

66122
A cutover changes only the `current` symlink and then restarts the service.

docs/V2-SESSION-HANDOFF.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# gajae-app v2 — Session Handoff (resume state)
22

3-
Last updated: 2026-09-05 (parallel correctness review and follow-up status). Supersedes the 2026-07-18 handoff.
3+
Last updated: 2026-09-05 (adversarial review and nine-skill live acceptance). Supersedes the 2026-07-18 handoff.
44

55
## TL;DR
66

@@ -474,6 +474,14 @@ after the reconcile fetch replaces realtime timestamps with disk ones.
474474

475475
## Current follow-ups (rechecked 2026-09-05)
476476

477+
- The subsequent adversarial pass and all nine live skill outcomes are in
478+
[`plans/adversarial-skills-e2e-2026-09-05.md`](plans/adversarial-skills-e2e-2026-09-05.md).
479+
DNS deployments now require explicit `ALLOWED_HOSTS`; see `SELF-HOST.md`.
480+
The app avoids the SDK workflow-ID defect by omitting the redundant explicit
481+
provider ID, and restores skill requests from transcript metadata. Delegation
482+
remains disabled because an offline real-SDK test proved that children bypass
483+
their parent's permission policy. Research/file artifacts do not mean that
484+
`ralplan`, `ultragoal` or the autoresearch goal lifecycle completed.
477485
- The parallel correctness pass is recorded in
478486
[`plans/code-review-2026-09-05.md`](plans/code-review-2026-09-05.md), including
479487
session/queue isolation, transcript turns and transport, scratch startup,
@@ -482,16 +490,20 @@ after the reconcile fetch replaces realtime timestamps with disk ones.
482490
start, and runtime edit-result diffs have shipped. The older unchecked
483491
public-distribution checklist in `V2-PLAN.md` is historical; signed and
484492
notarized beta.7/beta.8 images were already published on September 3.
485-
- **PR #30 / issue #18 remain blocked on the upstream SDK release.** The npm
493+
- **PR #30 still awaits the upstream SDK accessor fix.** The npm
486494
registry still reports `@gajae-code/coding-agent` latest `0.16.3`, which
487495
predates the workflow identity fix (`Yeachan-Heo/gajae-code#5282`, merged
488496
into `dev` at `2250239de9e565df2d3cb12ba365d65ff9f0555d`). Keep the existing
489497
`0.15.6` app pin until a published runtime carries that change, then finish
490-
the prepared regression test, manifest and dependency updates in #30.
491-
- **Issue #3 needs its skill matrix re-run after #30.** The bundled `gjc`
498+
the prepared regression test, manifest and dependency updates in #30. The
499+
app's redundant-ID workaround now removes the observed issue #18 trigger;
500+
it does not make the draft's direct SDK accessor contract pass.
501+
- **Issue #3 now has a live matrix, with unresolved capability blockers.** The bundled `gjc`
492502
shim is already in the app; the issue's older "shim in progress" comment
493-
is no longer current. Do not close the workflow issue based only on
494-
transport tests or on successful skill selection.
503+
is no longer current. Nine skills were actually invoked, including user
504+
skills. The interview produced its pending-approval spec; mandatory
505+
delegation and goal-mode restrictions still prevent complete execution of
506+
the other bundled workflows. Keep #3 open for those recorded limitations.
495507
- **CI signing awaits owner-provided credentials.** `gh secret list --env
496508
release` returned no environment secrets on September 5. The workflow
497509
and the five required names are documented in

docs/nginx-subpath-template.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
# location and rewrite together. The client currently keeps /api/, /ws, /shell
1010
# and /health at the site root, so they remain separately proxied.
1111
#
12+
# When publishing a DNS hostname, set ALLOWED_HOSTS to that hostname in the
13+
# app server's environment. HTTPS and server_name alone do not admit it;
14+
# see SELF-HOST.md for the migration from implicit same-host admission.
15+
#
1216
# Directives are scoped to the routes that need them: Accept-Encoding is
1317
# stripped only where sub_filter must read plaintext, upgrade headers travel
1418
# only on WebSocket-capable routes, and /health keeps the default timeouts.

0 commit comments

Comments
 (0)