refactor: share path-resolution and body-cap helpers (#355)

* refactor: share one helper for the per-user sif config directory

the modules loader and the framework custom-signature loader each
hand-rolled the same ~/.config/sif/<sub> (and %LocalAppData%\sif\<sub>
on windows) path. extract internal/sifpath.UserSubdir and route both
through it so the per-user layout is defined in one place.

no behavior change: the helper reproduces the existing paths exactly.

* refactor: read capped response bodies through one httpx helper

the frameworks detector and the module executor each defined their own
5 MB body cap and ran the same io.ReadAll(io.LimitReader(...)) read. move
the cap and the read into httpx.MaxBodySize / httpx.ReadCappedBody so both
scanners share one ceiling instead of two consts that can drift.

no behavior change: the cap value and read semantics are unchanged.
This commit is contained in:
Tigah
2026-07-22 15:48:49 -07:00
committed by GitHub
parent c2dbe4a19f
commit f6db61b0ef
7 changed files with 109 additions and 31 deletions
+2 -4
View File
@@ -26,11 +26,9 @@ import (
"time"
"github.com/tidwall/gjson"
"github.com/vmfunc/sif/internal/httpx"
)
// MaxBodySize limits response body to prevent memory exhaustion.
const MaxBodySize = 5 * 1024 * 1024
// ErrUnsupportedModuleType signals an executor for a module type that is not
// yet implemented. Returning it (rather than an empty result) keeps callers
// from mistaking "not implemented" for "scanned, found nothing".
@@ -422,7 +420,7 @@ func executeHTTPRequest(ctx context.Context, client *http.Client, r *httpRequest
defer resp.Body.Close()
// Read body with limit
respBody, err := io.ReadAll(io.LimitReader(resp.Body, MaxBodySize))
respBody, err := httpx.ReadCappedBody(resp)
if err != nil {
return Finding{}, false
}
+8 -13
View File
@@ -17,10 +17,10 @@ import (
"io/fs"
"os"
"path/filepath"
"runtime"
"github.com/charmbracelet/log"
"github.com/vmfunc/sif/internal/output"
"github.com/vmfunc/sif/internal/sifpath"
)
// builtinFS holds the modules embedded into the binary. it's set once, from the
@@ -45,20 +45,15 @@ type Loader struct {
// It automatically detects the built-in modules directory and sets up
// the user modules directory based on the operating system.
func NewLoader() (*Loader, error) {
home, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("get home dir: %w", err)
}
// resolveBuiltinDir already probes the executable dir, the working
// directory and the system data dirs, so the per-user lookup below is the
// only thing sifpath needs to own here.
builtinDir := resolveBuiltinDir()
// User modules directory based on OS
var userDir string
switch runtime.GOOS {
case "windows":
userDir = filepath.Join(home, "AppData", "Local", "sif", "modules")
default:
userDir = filepath.Join(home, ".config", "sif", "modules")
// User modules directory (can override built-ins)
userDir, err := sifpath.UserSubdir("modules")
if err != nil {
return nil, fmt.Errorf("resolve user modules dir: %w", err)
}
return &Loader{