diff options
| author | Christian Cleberg <[email protected]> | 2026-07-15 02:24:58 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-07-15 02:24:58 -0500 |
| commit | 7eb5e5e2230b6fb5b1bed6f0eaa03279aa61555b (patch) | |
| tree | 85e30ad9bf89809e3f2179123239d52bda81f63c /app/cli.go | |
| parent | 40d405f318a109aad5e7de7ad71fedcc04bb0e86 (diff) | |
| download | skunky-art-7eb5e5e2230b6fb5b1bed6f0eaa03279aa61555b.tar.gz skunky-art-7eb5e5e2230b6fb5b1bed6f0eaa03279aa61555b.tar.bz2 skunky-art-7eb5e5e2230b6fb5b1bed6f0eaa03279aa61555b.zip | |
fix: harden HTTP transport, server timeouts and panic paths
Correctness and security findings surfaced by golangci-lint, plus two
latent panics found alongside them.
- router: http.ListenAndServe has no timeouts at all (gosec G114), so a
slow client could hold a connection and its handler open indefinitely.
Replace it with an explicit http.Server carrying read/write/idle
timeouts.
- httpclient: InstallDAThrottle asserted http.DefaultTransport was a
*http.Transport and would panic outright if anything had already
wrapped it -- which is precisely what that function does. Check the
assertion and fall back to a fresh transport. Expose ProxiedTransport
so a configured download-proxy can inherit the same throttle and
timeouts instead of silently bypassing them.
- cache: the Sys() assertion to *syscall.Stat_t is only valid on unix and
would panic elsewhere; skip rotation instead. Indexing
Headers["Content-Type"][0] panics when the header is absent; use
Headers.Get. Cache files are written 0600 rather than 0700, as they are
never executed.
- cli, api: check error returns, and exit rather than nil-dereference a
file handle that failed to open.
SHA-1 and math/rand keep //nolint:gosec with reasons: they are cache-key
hashes and random-artwork picks, not security primitives.
Diffstat (limited to 'app/cli.go')
| -rwxr-xr-x | app/cli.go | 33 |
1 files changed, 23 insertions, 10 deletions
@@ -9,6 +9,9 @@ import ( "time" ) +// ExecuteCommandLineArguments parses argv, applying the flags that override +// config and running one-shot commands such as --help and --add-instance. Some +// of those commands exit the process rather than return. func ExecuteCommandLineArguments() { var helpmsg = `SkunkyArt v{{.Version}} [{{.Description}}] Usage: @@ -31,8 +34,12 @@ Copyright lost+skunk, X11. https://github.com/zerolabsco/skunky-art/releases/tag case "-h", "--help": var buf bytes.Buffer t := template.New("help") - t.Parse(helpmsg) - t.Execute(&buf, &Release) + tryWithExitStatus(func() error { + if _, err := t.Parse(helpmsg); err != nil { + return err + } + return t.Execute(&buf, &Release) + }(), 1) exit(buf.String(), 0) case "-a", "--add-instance": addInstance() @@ -79,13 +86,19 @@ func addInstance() { var settingsVar struct { Instances []settings `json:"instances"` } - instancesJson, err := os.OpenFile("instances.json", os.O_CREATE|os.O_WRONLY, 0644) - try(err) - defer instancesJson.Close() + // 0644: both files are committed to the repository and are meant to be + // world-readable, so gosec's 0600 default does not apply. + instancesJSON, err := os.OpenFile("instances.json", os.O_CREATE|os.O_WRONLY, 0644) //nolint:gosec // G302 + if err != nil { + exit(err.Error(), 1) + } + defer func() { try(instancesJSON.Close()) }() - instancesFile, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - try(err) - defer instancesFile.Close() + instancesFile, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //nolint:gosec // G302 + if err != nil { + exit(err.Error(), 1) + } + defer func() { try(instancesFile.Close()) }() for { if string(instances) == "" { @@ -113,7 +126,7 @@ func addInstance() { j, err := json.MarshalIndent(&settingsVar, "", " ") try(err) - instancesJson.Write(j) + try(func() error { _, err := instancesJSON.Write(j); return err }()) settingsVar := &settingsVar.Instances[len(settingsVar.Instances)-1] var mdstr bytes.Buffer @@ -157,7 +170,7 @@ func addInstance() { mdstr.WriteString(settingsVar.Country) mdstr.WriteString("|") - instancesFile.Write(mdstr.Bytes()) + try(func() error { _, err := instancesFile.Write(mdstr.Bytes()); return err }()) break } time.Sleep(500 * time.Millisecond) |
