### High — Background work is canceled as soon as the handler returns Relevant expression: `go s.runExport(r.Context(), id, req)` `net/http` cancels the request context when `ServeHTTP` returns. The accepted background job therefore receives a canceled context almost immediately. Downloads fail or are interrupted, but the loop continues and records the job as `"done"`, producing empty or partial exports. ### High — Concurrent access to `jobs` can crash the server Relevant expressions: `var jobs = map[string]string{}`, `jobs[id] = ...`, and `status, ok := jobs[id]` HTTP handlers and export goroutines read and write the same ordinary Go map without synchronization. Concurrent submissions or a status request racing with a job update can trigger a data race and the runtime fatal error `concurrent map read and map write` or `concurrent map writes`. ### High — Export names permit arbitrary file overwrite outside `exportDir` Relevant expressions: `path := filepath.Join(s.exportDir, req.Name)` and `os.Create(path)` A name containing traversal components such as `../../target` can escape `exportDir`. `os.Create` then creates or truncates that path, allowing a requester to overwrite any file writable by the process. ### High — Arbitrary URLs expose internal services to SSRF Relevant expression: `http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)` The server fetches caller-controlled URLs without scheme, host, address-range, or redirect validation. A requester can make it access loopback, link-local, or private-network HTTP endpoints and persist their responses in an export. ### Medium — Download and output failures are reported as successful jobs Relevant expressions: `continue` after request/client errors, `_, _ = io.Copy(out, resp.Body)`, and `jobs[id] = "done"` Invalid URLs, HTTP transport failures, canceled requests, disk-full errors, and interrupted writes are all ignored. The job reaches `"done"` even when one or every input failed, leaving an incomplete file with no recoverable failure status. ### Medium — HTTP error responses are written as normal export data Relevant expressions: `resp, err := http.DefaultClient.Do(downloadReq)` followed directly by `io.Copy(out, resp.Body)` Response status codes are never checked. A `404`, `500`, or authentication error page is appended to the export and the job is reported as done, so upstream failure becomes silently corrupted output. ### Medium — Response bodies remain open for the entire multi-URL job Relevant expression: `defer resp.Body.Close()` inside the loop Each defer runs only when `runExport` returns. A request containing many URLs therefore retains response-body and connection resources throughout the job, which can exhaust file descriptors or connection capacity before cleanup occurs. ### Medium — Downloads have no size bound Relevant expression: `io.Copy(out, resp.Body)` A URL can return an arbitrarily large or endless body. The job copies it until cancellation or I/O failure, allowing remote content to consume all available disk space; the resulting write failure is then ignored and marked done. ### Medium — Second-resolution IDs merge distinct jobs Relevant expression: `id := strconv.FormatInt(time.Now().Unix(), 10)` All exports accepted during the same second receive the same ID. Their status updates overwrite one another, so clients cannot identify their own job and may observe `"done"` or `"failed"` from a different export. ### Medium — Concurrent exports to the same name corrupt output Relevant expressions: `os.Create(path)` and caller-controlled `req.Name` Two accepted requests with the same name can open and truncate the same file while both goroutines write through independent file offsets. Their bytes can overwrite or interleave, and either job may report done despite corrupted output.