go-cache/docs/security-attack-vector-mapping.md
Virgil f9e1725c7d docs(cache): map security attack vectors
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-23 12:59:56 +00:00

8.2 KiB

Security Attack Vector Mapping

Scope: dappco.re/go/core/cache public API and backend read paths in cache.go. This package exposes a library surface only; it has no HTTP handlers or CLI argument parsing in-repo.

Function File:line Input source Flows into Current validation Potential attack vector
New cache.go:36 medium constructor argument from the consumer Stored on Cache.medium; used immediately by medium.EnsureDir(baseDir) and later by all Read/Write/Delete calls Only nil is replaced with coreio.Local; no capability or sandbox check in this package Backend policy bypass. A caller can supply an unsafe medium, and nil falls back to unsandboxed local filesystem access (io.Local is rooted at /), increasing the impact of later key or baseDir misuse.
New cache.go:36 baseDir constructor argument from the consumer medium.EnsureDir(baseDir); persisted on Cache.baseDir; later consumed by Path and Clear Empty string defaults to filepath.Join(cwd, ".core", "cache"); otherwise no normalization, allowlist, or sandbox enforcement in this package Arbitrary path selection. If baseDir is user-controlled or misconfigured, cache reads/writes/deletes can be redirected to attacker-chosen locations. With default io.Local, Clear can recurse-delete arbitrary directories other than / and $HOME, and Set can write cache JSON into unexpected filesystem locations.
New cache.go:41 Process working directory from os.Getwd() when baseDir == "" filepath.Join(cwd, ".core", "cache") No validation beyond Getwd succeeding Environment-controlled cache placement. Running the consumer from an attacker-influenced working directory redirects cache storage into that tree, which can expose data to other users/processes or alter which cache is later cleared.
New cache.go:36 ttl constructor argument from the consumer Stored on Cache.ttl; later used by time.Now().Add(c.ttl) in Set Only 0 is replaced with DefaultTTL; negative or very large durations are accepted Availability and data-staleness abuse. Negative TTL values force immediate misses; very large TTLs preserve stale or poisoned cache content longer than intended.
Path cache.go:68 key method argument from the caller filepath.Join(c.baseDir, key+".json"); returned path is later consumed by medium operations Resolves absBase and absPath and rejects results outside baseDir prefix Direct ../ traversal is blocked, but long or deeply nested keys can still create path-length issues, inode/file-count exhaustion, or namespace confusion within baseDir. Dot-segments and separators are normalized, which can collapse distinct logical keys into the same on-disk path.
Get cache.go:89 key method argument from the caller Path(key) then c.medium.Read(path) Inherits Path traversal guard Cache oracle and cross-tenant read risk inside the allowed namespace. An attacker who can choose keys can probe for existence/timing of other entries in a shared cache or read another principal's cached object if the consumer does not namespace keys.
Get cache.go:95 Backend content returned by c.medium.Read(path) json.Unmarshal([]byte(dataStr), &entry), expiry check, then json.Unmarshal(entry.Data, dest) Missing files become cache misses; invalid envelope JSON becomes a cache miss; there is no size limit, schema check, or integrity/authenticity check Malicious or compromised storage can feed oversized JSON for memory/CPU exhaustion, forge ExpiresAt far into the future to keep poisoned data live, or substitute crafted data payloads that alter downstream program behavior after unmarshal.
Get cache.go:89 dest method argument from the caller json.Unmarshal(entry.Data, dest) Relies entirely on Go's JSON decoder and the caller-provided destination type Type-driven resource abuse or logic confusion. If storage is attacker-controlled, decoding into permissive targets such as map[string]any, slices, or interfaces can trigger large allocations or smuggle unexpected structure into the consumer.
Set cache.go:123 key method argument from the caller Path(key), EnsureDir(filepath.Dir(path)), then Write(path, string(entryBytes)) Inherits Path traversal guard Namespace collision or storage exhaustion inside baseDir. An attacker-controlled key can create many directories/files, overwrite another tenant's cache entry, or consume disk/inodes within the permitted cache root.
Set cache.go:123 data method argument from the caller json.Marshal(data) into Entry.Data, then json.MarshalIndent(entry) and c.medium.Write(path, string(entryBytes)) Only successful JSON marshaling is required; no content, sensitivity, or size validation Large or adversarial objects can consume CPU/memory during marshal and write. Sensitive data is stored as plaintext JSON, and with the default local backend the write path uses default file mode 0644, creating local disclosure risk for cache contents.
Delete cache.go:158 key method argument from the caller Path(key) then c.medium.Delete(path) Inherits Path traversal guard; os.ErrNotExist is ignored Attacker-chosen eviction of entries inside baseDir. In a shared cache namespace this enables targeted cache invalidation or poisoning by deleting another principal's cached item.
Clear cache.go:175 c.baseDir set earlier by constructor input/environment c.medium.DeleteAll(c.baseDir) No validation at call time in this package Destructive recursive delete. If baseDir is user-controlled or misconfigured, Clear removes whatever tree the medium resolves that path to. With default unsandboxed io.Local, only / and $HOME are explicitly protected in the backend, leaving other directories in scope.
Age cache.go:183 key method argument from the caller Path(key) then c.medium.Read(path) Inherits Path traversal guard; any error returns -1 Metadata oracle within baseDir. An attacker can probe whether specific keys exist and silently suppress backend/path failures because all errors collapse to -1.
Age cache.go:189 Backend content returned by c.medium.Read(path) json.Unmarshal([]byte(dataStr), &entry) then time.Since(entry.CachedAt) Invalid JSON returns -1; no size limit or timestamp sanity check Malicious storage can return oversized JSON for resource exhaustion or forge timestamps, producing misleading negative or extreme ages that can distort caller refresh decisions.
GitHubReposKey cache.go:205 org argument from the caller filepath.Join("github", org, "repos"), typically later consumed as a cache key by Path/Set/Get No validation Key normalization and collision risk. Inputs containing separators or dot-segments are normalized by filepath.Join, so unexpected values can collapse into another logical cache key. Direct traversal only gets blocked later if the resulting key reaches Path.
GitHubRepoKey cache.go:210 org argument from the caller filepath.Join("github", org, repo, "meta") No validation Same collision/normalization issue as GitHubReposKey; a crafted org component can collapse onto another key path before the cache methods apply traversal checks.
GitHubRepoKey cache.go:210 repo argument from the caller filepath.Join("github", org, repo, "meta") No validation Same collision/normalization issue as the org input; crafted repo names containing separators or dot-segments can steer multiple logical repos onto the same cache key.

Notes

  • The package's strongest built-in control is the path-traversal guard in Cache.Path(). It protects Get, Set, Delete, and Age against simple ../ escapes relative to baseDir.
  • The highest-impact residual risk is not key traversal but unchecked control over baseDir and backend choice in New(), especially because the default coreio.Local medium is unsandboxed.
  • Read-side trust is weak by design: cache files are accepted without integrity protection, size limits, or schema validation, so any actor that can modify the backing medium can turn the cache into a poisoning or denial-of-service surface.