## Ticket: CVE-2026-42091 — goshs PUT upload CSRF (missing token + permissive CORS)

**Advisory**: GHSA-rhf7-wvw3-vjvm — https://github.com/patrickhener/goshs/security/advisories/GHSA-rhf7-wvw3-vjvm
**CVE**: CVE-2026-42091 | **CWE-352** (Cross-Site Request Forgery)
**Severity**: Medium — CVSS 3.1 base 6.5
**Package**: `github.com/patrickhener/goshs` (Go HTTP file-server CLI) | **Repository**: https://github.com/patrickhener/goshs

### Impact

The PUT upload handler in `httpserver/updown.go` lacks the CSRF token that
the POST upload handler requires. Combined with permissive CORS
(`Access-Control-Allow-Origin: *` on the OPTIONS preflight), any cross-origin
page the victim visits can issue an HTTP `PUT` and **write arbitrary files**
to a goshs server reachable from the victim's browser (loopback, LAN, etc.).

The fix adds CSRF-token validation to the PUT path so unauthenticated
cross-origin uploads are rejected.

### Affected / fixed versions

Affected: `goshs` `<= v2.0.1`.
Fixed: **`v2.0.2`**.

Reproduce on a vulnerable build (**`v2.0.1`**) and verify the fix on
**`v2.0.2`**.

### Where to look

The fix ships in commit
[`0e715b94e10c3d1aa552276000f15f104dee2f32`](https://github.com/patrickhener/goshs/commit/0e715b94e10c3d1aa552276000f15f104dee2f32).
Inspect the patch to confirm the root cause and the CSRF guard added on PUT:

```bash
git clone https://github.com/patrickhener/goshs.git
cd goshs && git show 0e715b94e10c3d1aa552276000f15f104dee2f32
```

### Reproduction approach

No browser is required — the bug is that the server accepts a CSRF-tokenless
PUT at all, which can be exercised directly with `curl` (modeling the
cross-origin PUT the browser would send).

1. Build the **vulnerable** binary:
   `git clone https://github.com/patrickhener/goshs && cd goshs && git checkout v2.0.1 && go build ./...`
2. Start it in a scratch upload directory with upload enabled:
   `./goshs -u -p <port>`
3. From a separate shell, issue the cross-origin-style PUT:
   `curl -i -X PUT --data-binary 'pwned' http://localhost:<port>/pwned.txt`
4. Check the upload directory for `pwned.txt`.
5. Repeat steps 1-4 with the **fixed** build (`git checkout v2.0.2`).

### Expected result

| Build | `PUT /pwned.txt` (no CSRF token) | `pwned.txt` in upload dir |
|-------|-----------------------------------|---------------------------|
| `goshs v2.0.1` (vulnerable) | accepted (HTTP 2xx) | **created** — CSRF write confirmed |
| `goshs v2.0.2` (fixed) | rejected (**HTTP 403**) by new CSRF check | **not created** |

- **Vulnerable indicator**: PUT returns 2xx and the file `pwned.txt` is
  present in the upload directory after the request.
- **Fixed indicator**: PUT returns HTTP 403 and no `pwned.txt` is written.

### Expected artifacts

- `reproduction_steps.sh` — builds both versions, starts goshs in a scratch
  upload dir, fires the curl PUT, and asserts the HTTP status and the
  presence/absence of `pwned.txt` for each.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable and
  fixed indicators captured.
- Logs capturing the HTTP response and upload-directory contents for
  `v2.0.1` and `v2.0.2`.
