go-io/docs/security-attack-vector-mapping.md
2026-03-23 13:20:41 +00:00

33 KiB

Security Attack Vector Mapping

CODEX.md was not present under /workspace, so this mapping follows CLAUDE.md and the current source tree.

Scope:

  • Included: exported functions and methods that accept caller-controlled data or parse external payloads, plus public writer types returned from those APIs.
  • Omitted: zero-argument accessors and teardown helpers such as Close, Snapshot, Store, AsMedium, DataNode, and fs.FileInfo getters because they are not ingress points.

Notes:

  • local is the in-repo filesystem containment layer. Its protection depends on validatePath, but most mutating operations still have a post-validation TOCTOU window before the final os.* call.
  • workspace.Service uses io.Local rooted at /, so its path joins are not sandboxed by this repository.
  • datanode.FromTar and datanode.Restore inherit Borg datanode.FromTar behavior from forge.lthn.ai/Snider/Borg v0.3.1: it trims leading /, preserves symlink tar entries, and does not reject .. segments or large archives.

io Facade And MockMedium

Function File:Line Input source What it flows into Current validation Potential attack vector
io.NewSandboxed io.go:126 Caller-supplied sandbox root Delegates to local.New(root) and stores the resolved root in a local.Medium local.New absolutizes and best-effort resolves root symlinks; no policy check on / or broad roots Misconfiguration can disable containment entirely by choosing / or an overly broad root
io.Read io.go:133 Caller path plus chosen backend Direct m.Read(path) dispatch No facade-level validation Inherits backend read, enumeration, and path-handling attack surface
io.Write io.go:138 Caller path/content plus chosen backend Direct m.Write(path, content) dispatch No facade-level validation Inherits backend overwrite, creation, and storage-exhaustion attack surface
io.ReadStream io.go:143 Caller path plus chosen backend Direct m.ReadStream(path) dispatch No facade-level validation Inherits backend streaming-read surface and any unbounded downstream consumption risk
io.WriteStream io.go:148 Caller path plus chosen backend; later streamed bytes from returned writer Direct m.WriteStream(path) dispatch No facade-level validation Inherits backend streaming-write surface, including arbitrary object/file creation and unbounded buffering/disk growth
io.EnsureDir io.go:153 Caller path plus chosen backend Direct m.EnsureDir(path) dispatch No facade-level validation Inherits backend directory-creation semantics; on no-op backends this can create false assumptions about isolation
io.IsFile io.go:158 Caller path plus chosen backend Direct m.IsFile(path) dispatch No facade-level validation Inherits backend existence-oracle and metadata-disclosure surface
io.Copy io.go:163 Caller-selected source/destination mediums and paths src.Read(srcPath) loads full content into memory, then dst.Write(dstPath, content) Validation delegated to both backends Large source content can exhaust memory; can bridge trust zones and copy attacker-controlled names/content across backends
(*io.MockMedium).Read, FileGet, Open, ReadStream, List, Stat, Exists, IsFile, IsDir io.go:193, 225, 358, 388, 443, 552, 576, 219, 587 Caller path Direct map lookup and prefix scans in in-memory maps No normalization, auth, or path restrictions If reused outside tests, it becomes a trivial key/value disclosure and enumeration surface
(*io.MockMedium).Write, WriteMode, FileSet, EnsureDir io.go:202, 208, 230, 213 Caller path/content/mode Direct map writes; WriteMode ignores mode No validation; permissions are ignored Arbitrary overwrite/creation of entries and silent permission-policy bypass
(*io.MockMedium).Create, Append, WriteStream, (*io.MockWriteCloser).Write io.go:370, 378, 393, 431 Caller path; streamed caller bytes Buffers bytes in memory until Close, then commits to Files[path] No validation or size limits Memory exhaustion and arbitrary entry overwrite if used as anything other than a test double
(*io.MockMedium).Delete, DeleteAll, Rename io.go:235, 263, 299 Caller path(s) Direct map mutation and prefix scans No normalization or authorization Arbitrary delete/rename of entries; prefix-based operations can remove more than a caller expects

local

Function File:Line Input source What it flows into Current validation Potential attack vector
local.New local/client.go:24 Caller-supplied root path filepath.Abs, optional filepath.EvalSymlinks, stored as Medium.root Absolutizes root and resolves root symlink when possible Passing / creates unsandboxed host filesystem access; broad roots widen blast radius
(*local.Medium).Read, FileGet local/client.go:114, 300 Caller path validatePath then os.ReadFile validatePath cleans path, walks symlinks component-by-component, and blocks resolved escapes from root Arbitrary read of anything reachable inside the sandbox; TOCTOU symlink swap remains possible after validation and before the final read
(*local.Medium).Open, ReadStream local/client.go:210, 248 Caller path validatePath then os.Open; ReadStream delegates to Open Same validatePath containment check Same read/disclosure surface as Read, plus a validated path can still be swapped before os.Open
(*local.Medium).List, Stat, Exists, IsFile, IsDir local/client.go:192, 201, 182, 169, 156 Caller path validatePath then os.ReadDir or os.Stat Same validatePath containment check Metadata enumeration for any path inside the sandbox; TOCTOU can still skew the checked object before the final syscall
(*local.Medium).Write, FileSet local/client.go:129, 305 Caller path/content Delegates to WriteMode(..., 0644) Path containment only Arbitrary overwrite inside the sandbox; default 0644 can expose secrets if higher layers use it for sensitive data
(*local.Medium).WriteMode local/client.go:135 Caller path/content/mode validatePath, os.MkdirAll, os.WriteFile Path containment only; caller controls file mode Arbitrary file write inside the sandbox; caller can choose overly broad modes; TOCTOU after validation can retarget the write
(*local.Medium).Create, WriteStream, Append local/client.go:219, 258, 231 Caller path; later bytes written through the returned *os.File validatePath, os.MkdirAll, os.Create or os.OpenFile(..., O_APPEND) Path containment only Arbitrary truncate/append within the sandbox, unbounded disk growth, and the same post-validation race window
(*local.Medium).EnsureDir local/client.go:147 Caller path validatePath then os.MkdirAll Path containment only Arbitrary directory creation inside the sandbox; TOCTOU race can still redirect the mkdir target
(*local.Medium).Delete local/client.go:263 Caller path validatePath then os.Remove Path containment; explicit guard blocks / and $HOME Arbitrary file or empty-dir deletion inside the sandbox; guard does not protect other critical paths if root is too broad; TOCTOU applies
(*local.Medium).DeleteAll local/client.go:275 Caller path validatePath then os.RemoveAll Path containment; explicit guard blocks / and $HOME Recursive delete of any sandboxed subtree; if the medium root is broad, the blast radius is broad too
(*local.Medium).Rename local/client.go:287 Caller old/new paths validatePath on both sides, then os.Rename Path containment on both paths Arbitrary move/overwrite inside the sandbox; attacker-controlled rename targets can be swapped after validation

sqlite

Function File:Line Input source What it flows into Current validation Potential attack vector
sqlite.WithTable sqlite/sqlite.go:29 Caller-supplied table name option Stored on Medium.table and concatenated into every SQL statement No quoting or identifier validation SQL injection or malformed SQL if an attacker can choose the table name
sqlite.New sqlite/sqlite.go:37 Caller DB path/URI and options sql.Open("sqlite", dbPath), PRAGMA, CREATE TABLE using concatenated table name Rejects empty dbPath; no table-name validation Arbitrary SQLite file/URI selection and inherited SQL injection risk from WithTable
(*sqlite.Medium).Read, FileGet, Open, ReadStream sqlite/sqlite.go:94, 172, 455, 521 Caller path cleanPath then parameterized SELECT; Open/ReadStream materialize the whole BLOB in memory Leading-slash path.Clean collapses traversal and rejects empty/root keys; path value is parameterized, table name is not Arbitrary logical-key read, existence disclosure, canonicalization collisions such as ../x -> x, and memory exhaustion on large BLOBs
(*sqlite.Medium).Write, FileSet sqlite/sqlite.go:118, 177 Caller path/content cleanPath then parameterized upsert Same path normalization; table name still concatenated Arbitrary logical-key overwrite and unbounded DB growth; different raw paths can alias to the same normalized key
(*sqlite.Medium).Create, WriteStream, Append, (*sqlite.sqliteWriteCloser).Write sqlite/sqlite.go:487, 546, 499, 654 Caller path; streamed caller bytes cleanPath, optional preload of existing BLOB, in-memory buffering, then upsert on Close Non-empty normalized key only Memory exhaustion from buffering and append preloads; arbitrary overwrite/append of normalized keys
(*sqlite.Medium).EnsureDir sqlite/sqlite.go:136 Caller path cleanPath then inserts a directory marker row Root becomes a no-op; other paths are normalized only Arbitrary logical directory creation and aliasing through normalized names
(*sqlite.Medium).List, Stat, Exists, IsFile, IsDir sqlite/sqlite.go:349, 424, 551, 155, 569 Caller path cleanPath then parameterized listing/count/stat queries Same normalized key handling; table name still concatenated Namespace enumeration and metadata disclosure; canonicalization collisions can hide the caller's original path spelling
(*sqlite.Medium).Delete sqlite/sqlite.go:182 Caller path cleanPath, directory-child count, then DELETE Rejects empty/root path and non-empty dirs Arbitrary logical-key deletion
(*sqlite.Medium).DeleteAll sqlite/sqlite.go:227 Caller path cleanPath then DELETE WHERE path = ? OR path LIKE ? Rejects empty/root path Bulk deletion of any logical subtree
(*sqlite.Medium).Rename sqlite/sqlite.go:251 Caller old/new paths cleanPath both paths, then transactional copy/delete of entry and children Requires non-empty normalized source and destination Arbitrary move/overwrite of logical subtrees; normalized-path aliasing can redirect or collapse entries

s3

Function File:Line Input source What it flows into Current validation Potential attack vector
s3.WithPrefix s3/s3.go:44 Caller-supplied prefix Stored on Medium.prefix and prepended to every key Only ensures a trailing / when non-empty Cross-tenant namespace expansion or contraction if untrusted callers can choose the prefix; empty prefix exposes the whole bucket
s3.WithClient s3/s3.go:55 Caller-supplied S3 client Stored as Medium.client and trusted for all I/O No validation Malicious or wrapped clients can exfiltrate data, fake results, or bypass expected transport controls
s3.New s3/s3.go:69 Caller bucket name and options Stores bucket/prefix/client on Medium Rejects empty bucket and missing client only Redirecting operations to attacker-chosen buckets or prefixes if config is not trusted
(*s3.Medium).EnsureDir s3/s3.go:144 Caller path (ignored) No-op Input is ignored entirely Semantic mismatch: callers may believe a directory boundary now exists when S3 still has only object keys
(*s3.Medium).Read, FileGet, Open s3/s3.go:103, 166, 388 Caller path key(p) then GetObject; Read/Open read the whole body into memory Leading-slash path.Clean keeps the key under prefix; rejects empty key Arbitrary read inside the configured bucket/prefix, canonicalization collisions, and memory exhaustion on large objects
(*s3.Medium).ReadStream s3/s3.go:464 Caller path key(p) then GetObject, returning the raw response body Same normalized key handling; no size/content checks Delivers arbitrary remote object bodies to downstream consumers without integrity, type, or size enforcement
(*s3.Medium).Write, FileSet s3/s3.go:126, 171 Caller path/content key(p) then PutObject Same normalized key handling Arbitrary object overwrite or creation within the configured prefix
(*s3.Medium).Create, WriteStream, Append, (*s3.s3WriteCloser).Write s3/s3.go:427, 481, 440, 609 Caller path; streamed caller bytes key(p), optional preload of existing object for append, in-memory buffer, then PutObject on Close Non-empty normalized key only Memory exhaustion from buffering and append preloads; arbitrary overwrite/append of objects under the prefix
(*s3.Medium).List, Stat, Exists, IsFile, IsDir s3/s3.go:282, 355, 486, 149, 518 Caller path key(p) then ListObjectsV2 or HeadObject Normalized key stays under prefix; no authz or tenancy checks beyond config Namespace enumeration and metadata disclosure across any objects reachable by the configured prefix
(*s3.Medium).Delete s3/s3.go:176 Caller path key(p) then DeleteObject Non-empty normalized key only Arbitrary object deletion inside the configured prefix
(*s3.Medium).DeleteAll s3/s3.go:193 Caller path key(p), then exact delete plus prefix-based ListObjectsV2 and batched DeleteObjects Non-empty normalized key only Bulk deletion of every object under a caller-chosen logical subtree
(*s3.Medium).Rename s3/s3.go:252 Caller old/new paths key(p) on both paths, then CopyObject followed by DeleteObject Non-empty normalized keys only Arbitrary move/overwrite of objects within the configured prefix; special characters in oldPath can also make CopySource handling fragile

store

store.Store

Function File:Line Input source What it flows into Current validation Potential attack vector
store.New store/store.go:22 Caller DB path/URI sql.Open("sqlite", dbPath), PRAGMA, schema creation No validation beyond driver errors Arbitrary SQLite file/URI selection if configuration is attacker-controlled
(*store.Store).Get store/store.go:49 Caller group/key Parameterized SELECT value FROM kv WHERE grp = ? AND key = ? Uses placeholders; no group/key policy Arbitrary secret/config disclosure for any reachable group/key
(*store.Store).Set store/store.go:62 Caller group/key/value Parameterized upsert into kv Uses placeholders; no group/key policy Arbitrary overwrite or creation of stored values
(*store.Store).Delete, DeleteGroup store/store.go:75, 94 Caller group and optional key Parameterized DELETE statements Uses placeholders; no authorization or namespace policy Single-key or whole-group deletion
(*store.Store).Count, GetAll store/store.go:84, 103 Caller group Parameterized count or full scan of the group Uses placeholders; no access control Group enumeration and bulk disclosure of every key/value in a group
(*store.Store).Render store/store.go:125 Caller template string and group name Loads all group values into a map, then template.Parse and template.Execute No template allowlist or output escaping; template funcs are default-only Template-driven exfiltration of all values in the chosen group; downstream output injection if rendered text is later used in HTML, shell, or config sinks

store.Medium

Function File:Line Input source What it flows into Current validation Potential attack vector
store.NewMedium store/medium.go:23 Caller DB path/URI Delegates to store.New(dbPath) No extra validation Same arbitrary-DB selection risk as store.New
(*store.Medium).EnsureDir store/medium.go:80 Caller path (ignored) No-op Input is ignored Semantic mismatch: callers may assume they created a boundary when the store still treats group creation as implicit
(*store.Medium).Read, FileGet, Open, ReadStream store/medium.go:62, 95, 214, 246 Caller medium path splitPath then Store.Get; Open/ReadStream materialize value bytes or a string reader path.Clean, strip leading /, require group/key; does not forbid odd group names like .. Arbitrary logical-key disclosure and group/key aliasing if higher layers treat raw paths as identity
(*store.Medium).Write, FileSet store/medium.go:71, 100 Caller path/content splitPath then Store.Set Same group/key check only Arbitrary overwrite of any reachable group/key
(*store.Medium).Create, WriteStream, Append, (*store.kvWriteCloser).Write store/medium.go:227, 259, 236, 343 Caller path; streamed caller bytes splitPath, optional preload of existing value for append, in-memory buffer, then Store.Set on Close Requires group/key; no size limit Memory exhaustion and arbitrary value overwrite/append
(*store.Medium).Delete store/medium.go:105 Caller path splitPath; group-only paths call Count, group/key paths call Store.Delete Rejects empty path; refuses non-empty group deletes Arbitrary single-key deletion and group-existence probing
(*store.Medium).DeleteAll store/medium.go:124 Caller path splitPath; group-only paths call DeleteGroup, group/key paths call Delete Rejects empty path Whole-group deletion or single-key deletion
(*store.Medium).Rename store/medium.go:136 Caller old/new paths splitPath, Store.Get, Store.Set, Store.Delete Requires both paths to include group/key Arbitrary cross-group data movement and destination overwrite
(*store.Medium).List store/medium.go:154 Caller path Empty path lists groups; group path loads all keys via GetAll splitPath only; no auth Group and key enumeration; value lengths leak through returned file info sizes
(*store.Medium).Stat, Exists, IsFile, IsDir store/medium.go:191, 264, 85, 278 Caller path splitPath, then Count or Get Same splitPath behavior Existence oracle and metadata disclosure for groups and keys

node

Function File:Line Input source What it flows into Current validation Potential attack vector
node.AddData node/node.go:40 Caller file name and content Stores name as a map key and content as in-memory bytes Strips a leading /; ignores empty names and trailing /; does not clean . or .. Path-confusion payloads such as ../x or ./x persist verbatim and can later become traversal gadgets when copied out or tarred
node.FromTar, (*node.Node).LoadTar node/node.go:84, 93 Caller-supplied tar archive bytes archive/tar reader, io.ReadAll per regular file, then newFiles[name] = ... Trims a leading /; ignores empty names and directory entries; no path.Clean, no .. rejection, no size limits Tar-slip-style names survive in memory and can be exported later; huge or duplicate entries can exhaust memory or overwrite earlier entries
(*node.Node).Read, FileGet, ReadFile, Open, ReadStream node/node.go:349, 370, 187, 259, 491 Caller path/name Direct map lookup or directory inference; Read and ReadFile copy/convert content to memory Only strips a leading / Arbitrary access to weird literal names and confusion if callers assume canonical path handling
(*node.Node).Write, WriteMode, FileSet node/node.go:359, 365, 375 Caller path/content/mode Delegates to AddData; WriteMode ignores mode Same minimal trimming as AddData Arbitrary overwrite of any key, including attacker-planted ../ names; false sense of permission control
(*node.Node).Create, WriteStream, Append, (*node.nodeWriter).Write node/node.go:473, 500, 480, 513 Caller path; streamed caller bytes Buffer bytes in memory and commit them as a map entry on Close Only strips a leading /; no size limit Memory exhaustion and creation of path-confusion payloads that can escape on later export
(*node.Node).Delete, DeleteAll, Rename node/node.go:411, 421, 445 Caller path(s) Direct map mutation keyed by caller-supplied names Only strips a leading / Arbitrary delete/rename of any key, including ../-style names; no directory-safe rename logic
(*node.Node).Stat, List, ReadDir, Exists, IsFile, IsDir node/node.go:278, 461, 297, 387, 393, 400 Caller path/name Directory inference from map keys and fs adapter methods Only strips a leading / Namespace enumeration and ambiguity around equivalent-looking path spellings
(*node.Node).WalkNode, Walk node/node.go:128, 145 Caller root path, callback, filters fs.WalkDir over the in-memory tree No root normalization beyond whatever Node already does Attackers who can plant names can force callback traversal over weird paths; SkipErrors can suppress unexpected failures
(*node.Node).CopyFile node/node.go:200 Caller source key, destination host path, permissions Reads node content and calls os.WriteFile(dst, ...) directly Only checks that src exists and is not a directory Arbitrary host filesystem write to a caller-chosen dst path
(*node.Node).CopyTo node/node.go:218 Caller target medium, source path, destination path Reads node entries and calls target.Write(destPath or destPath/rel, content) Only checks that the source exists Stored ../-style node keys can propagate into destination paths, enabling traversal or overwrite depending on the target backend
(*node.Node).EnsureDir node/node.go:380 Caller path (ignored) No-op Input is ignored Semantic mismatch: callers may assume a directory boundary was created when directories remain implicit

datanode

Function File:Line Input source What it flows into Current validation Potential attack vector
datanode.FromTar, (*datanode.Medium).Restore datanode/client.go:41, 65 Caller-supplied tar archive bytes Delegates to Borg datanode.FromTar(data) and replaces the in-memory filesystem Wrapper adds no checks; inherited Borg behavior trims leading / only and accepts symlink tar entries Archive bombs, preserved symlink entries, and ../-style names can be restored into the in-memory tree
(*datanode.Medium).Read, FileGet, Open, ReadStream datanode/client.go:97, 175, 394, 429 Caller path clean(p) then dn.Open/dn.Stat; Read loads the full file into memory clean strips a leading / and runs path.Clean, but it does not sandbox .. at the start of the path Arbitrary logical-key reads, including odd names such as ../x; full reads can exhaust memory on large files
(*datanode.Medium).Write, WriteMode, FileSet datanode/client.go:123, 138, 179 Caller path/content/mode clean(p), then dn.AddData and explicit parent-dir tracking Rejects empty path only; WriteMode ignores mode Arbitrary overwrite/creation of logical entries, including ../-style names; canonicalization can also collapse some raw paths onto the same key
(*datanode.Medium).Create, WriteStream, Append, (*datanode.writeCloser).Write datanode/client.go:402, 441, 410, 540 Caller path; streamed caller bytes clean(p), optional preload of existing data for append, in-memory buffer, then dn.AddData on Close Rejects empty path; no size limit Memory exhaustion and arbitrary overwrite/append of logical entries
(*datanode.Medium).EnsureDir datanode/client.go:142 Caller path clean(p) then marks explicit directories in m.dirs Empty path becomes a no-op; no policy on ..-style names Arbitrary logical directory creation and enumeration under attacker-chosen names
(*datanode.Medium).Delete datanode/client.go:183 Caller path clean(p), then file removal or explicit-dir removal Blocks deleting the empty/root path; otherwise no path policy Arbitrary logical deletion of files or empty directories
(*datanode.Medium).DeleteAll datanode/client.go:220 Caller path clean(p), then subtree walk and removal Blocks deleting the empty/root path Recursive deletion of any logical subtree
(*datanode.Medium).Rename datanode/client.go:262 Caller old/new paths clean both paths, then read-add-delete for files or subtree move for dirs Existence checks only; no destination restrictions Arbitrary subtree move/overwrite, including ../-style names that later escape on export or copy-out
(*datanode.Medium).List, Stat, Exists, IsFile, IsDir datanode/client.go:327, 374, 445, 166, 460 Caller path clean(p), then dn.ReadDir/dn.Stat/explicit-dir map lookups Same non-sandboxing clean behavior Namespace enumeration and metadata disclosure for weird or traversal-looking logical names

workspace

Function File:Line Input source What it flows into Current validation Potential attack vector
workspace.New workspace/service.go:41 Caller core.Core and optional cryptProvider Resolves $HOME, sets rootPath = ~/.core/workspaces, and binds medium = io.Local Ensures the root directory exists; no sandboxing because io.Local is rooted at / All later workspace path joins operate on the real host filesystem, not a project sandbox
(*workspace.Service).CreateWorkspace workspace/service.go:68 Caller identifier and password SHA-256 hashes identifier into wsID, creates directories under rootPath, calls crypt.CreateKeyPair, writes keys/private.key Requires crypt to exist, checks for workspace existence, writes key with mode 0600; no password policy or identifier validation Predictable unsalted workspace IDs can leak identifier privacy through offline guessing; creates real host directories/files if exposed remotely
(*workspace.Service).SwitchWorkspace workspace/service.go:103 Caller workspace name filepath.Join(rootPath, name) then medium.IsDir, stores activeWorkspace = name Only checks that the joined path currently exists as a directory Path traversal via name can escape rootPath and bind the service to arbitrary host directories
(*workspace.Service).WorkspaceFileGet workspace/service.go:126 Caller filename activeFilePath uses filepath.Join(rootPath, activeWorkspace, "files", filename), then medium.Read Only checks that an active workspace is set; no filename containment check filename can escape the files/ directory, and a malicious activeWorkspace can turn reads into arbitrary host-file access
(*workspace.Service).WorkspaceFileSet workspace/service.go:138 Caller filename and content Same activeFilePath join, then medium.Write Only checks that an active workspace is set; no filename containment check Arbitrary host-file write if activeWorkspace or filename contains traversal segments
(*workspace.Service).HandleIPCEvents workspace/service.go:150 Untrusted core.Message payload, typically map[string]any from IPC Extracts "action" and dispatches to CreateWorkspace or SwitchWorkspace Only loose type assertions; no schema, authz, or audit response on failure Remote IPC callers can trigger workspace creation or retarget the service to arbitrary directories because downstream helpers do not enforce containment

sigil

Function File:Line Input source What it flows into Current validation Potential attack vector
sigil.Transmute sigil/sigil.go:46 Caller data bytes and sigil chain Sequential Sigil.In calls No chain policy; relies on each sigil Attacker-chosen chains can trigger expensive transforms or weaken policy if callers let the attacker choose the sigils
sigil.Untransmute sigil/sigil.go:62 Caller data bytes and sigil chain Reverse-order Sigil.Out calls No chain policy; relies on each sigil Expensive or mismatched reverse chains can become a CPU/memory DoS surface
(*sigil.ReverseSigil).In, Out sigil/sigils.go:29, 41 Caller data bytes Allocates a new buffer and reverses it Nil-safe only Large inputs allocate a second full-sized buffer; otherwise low risk
(*sigil.HexSigil).In, Out sigil/sigils.go:50, 60 Caller data bytes Hex encode/decode into fresh buffers Nil-safe only; decode returns errors from hex.Decode Large or malformed input can still drive allocation and CPU usage
(*sigil.Base64Sigil).In, Out sigil/sigils.go:74, 84 Caller data bytes Base64 encode/decode into fresh buffers Nil-safe only; decode returns errors from StdEncoding.Decode Large or malformed input can still drive allocation and CPU usage
(*sigil.GzipSigil).In sigil/sigils.go:100 Caller data bytes gzip.NewWriter, compression into a bytes.Buffer Nil-safe only Large input can consume significant CPU and memory while compressing
(*sigil.GzipSigil).Out sigil/sigils.go:120 Caller compressed bytes gzip.NewReader then io.ReadAll Nil-safe only; malformed gzip errors out Zip-bomb style payloads can decompress to unbounded memory
(*sigil.JSONSigil).In, Out sigil/sigils.go:137, 149 Caller JSON bytes json.Compact/json.Indent; Out is a pass-through No schema validation; Out does nothing Large inputs can consume CPU/memory; callers may wrongly assume Out validates or normalizes JSON
sigil.NewHashSigil, (*sigil.HashSigil).In, Out sigil/sigils.go:161, 166, 215 Caller hash enum and data bytes Selects a hash implementation, hashes input, and leaves Out as pass-through Unsupported hashes error out; weak algorithms are still allowed If algorithm choice is attacker-controlled, callers can be downgraded to weak digests such as MD4/MD5/SHA1; large inputs can still be CPU-heavy
sigil.NewSigil sigil/sigils.go:221 Caller sigil name Factory switch returning encoding, compression, formatting, hashing, or weak hash sigils Fixed allowlist only If exposed as user config, attackers can select weak or semantically wrong transforms and bypass higher-level crypto expectations
(*sigil.XORObfuscator).Obfuscate, Deobfuscate sigil/crypto_sigil.go:65, 73 Caller data and entropy bytes SHA-256-derived keystream then XOR over a full-size output buffer No validation Safe only as a subroutine; if misused as standalone protection, it is merely obfuscation and still a CPU/memory surface on large input
(*sigil.ShuffleMaskObfuscator).Obfuscate, Deobfuscate sigil/crypto_sigil.go:127, 154 Caller data and entropy bytes Deterministic permutation and XOR-mask over full-size buffers No validation Large inputs drive multiple full-size allocations and CPU work; still only obfuscation if used outside authenticated encryption
sigil.NewChaChaPolySigil sigil/crypto_sigil.go:247 Caller key bytes Copies key into ChaChaPolySigil state Validates only that the key is exactly 32 bytes Weak but correctly-sized keys are accepted; long-lived key material stays resident in process memory
sigil.NewChaChaPolySigilWithObfuscator sigil/crypto_sigil.go:263 Caller key bytes and custom obfuscator Builds a ChaChaPolySigil and optionally swaps the obfuscator Key length is validated; obfuscator is trusted if non-nil Malicious or buggy obfuscators can break the intended defense-in-depth model or leak patterns
(*sigil.ChaChaPolySigil).In sigil/crypto_sigil.go:276 Caller plaintext bytes rand.Reader nonce, optional obfuscation, then chacha20poly1305.Seal Requires a configured key; nil input is allowed Large plaintexts allocate full ciphertexts; if randReader is replaced in tests or DI, nonce quality becomes attacker-influenced
(*sigil.ChaChaPolySigil).Out sigil/crypto_sigil.go:315 Caller ciphertext bytes Nonce extraction, aead.Open, optional deobfuscation Requires a configured key, checks minimum length, and relies on AEAD authentication Primarily a CPU DoS surface on repeated bogus ciphertext; integrity is otherwise strong
sigil.GetNonceFromCiphertext sigil/crypto_sigil.go:359 Caller ciphertext bytes Copies the first 24 bytes as a nonce Length check only Low-risk parser surface; malformed short inputs just error