You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Harden app-store install path and cap-state reads (#315)
* Harden app-store install path and cap-state reads
Three install-path security fixes in pilotctl appstore:
- Path traversal during staging: manifest binary.path was joined onto
the bundle and staging dirs with no containment check, so a path like
../../etc/x escaped the staging tree. Add a resolveUnder guard
(mirrors the supervisor's) at both join sites; reject any path that
resolves outside the target dir.
- Unbounded catalogue bundle download: the bundle tarball was copied
with no size limit, so a hostile or compromised CDN could exhaust disk
before the post-copy sha256 check ran. Cap the compressed download
with io.LimitReader (maxBundleBytes) and fail closed when exceeded.
- Cap-state reads no longer fail open: loadCapStateRecords silently
skipped malformed lines and tampered HMAC records, under-reporting
usage and hiding an erased cap. It now returns an error and the caps
command fails closed on a malformed line, an HMAC mismatch, or a
signed chain spliced with an unauthenticated record.
Tests: ../../etc/x and absolute/empty paths rejected; oversized bundle
download refused; malformed line and tampered/mixed cap-state records
fail closed; legacy and nil-key reads still accepted.
* Confine appstore caps app id and annotate guarded gosec paths
---------
Co-authored-by: Teodor Calin <teodor@vulturelabs.io>
// #nosec G703 -- stagingDir is appStoreRoot()/<m.ID>.staging; m.ID is reverse-DNS validated by m.Validate() above, so it cannot escape the install root
1163
+
_=os.RemoveAll(stagingDir)
1164
+
fatalHint("invalid_argument",
1165
+
"manifest binary.path must stay inside the staging dir",
// Fail closed: a tampered or corrupt spend log must surface as
1584
+
// an error, not be papered over with an under-reported usage
1585
+
// figure that makes an erased cap look healthy.
1586
+
fatalHint("integrity_error",
1587
+
"the wallet's cap-state spend log failed its integrity check; do not trust reported usage. Inspect cap-state.jsonl and, if the wallet's identity is intact, let the wallet rewrite it.",
1588
+
"cap-state: %v", err)
1589
+
}
1532
1590
1533
1591
now:=time.Now()
1534
1592
reports:=make([]capUsageReport, 0, len(caps))
@@ -1762,21 +1820,39 @@ type capStateRecord struct {
1762
1820
hmacOKbool
1763
1821
}
1764
1822
1765
-
// loadCapStateRecords reads the wallet's persistent spend log.
1766
-
// When hmacKey is non-nil, records with a "hmac" field are verified
1767
-
// against the chained HMAC-SHA256; tampered records are skipped.
1768
-
// Records without "hmac" pass through (backward-compat).
0 commit comments