Documentation
¶
Overview ¶
Package fileblock is the file-type blocking engine: an extension/MIME/ Content-Disposition blocklist (FileBlocker) and named file-type profiles (FileProfileStore, in fileprofile.go). It depends only on the shared seam (internal/obs for logging, internal/fileutil for atomic persistence) and the standard library — no dependency on the rest of Culvert (ADR-0002 leaf, unblocked by the ADR-0003 seam).
Index ¶
- Variables
- func BlockConn(dst interface{ ... }, host, urlPath, ext, source string)
- func BlockMessage(ext, source string) string
- func ExtractCDFilename(cd string) string
- func LogBlock(host, urlPath, ext, source string)
- type FileBlocker
- func (fb *FileBlocker) Add(ext string)
- func (fb *FileBlocker) CheckContentDisposition(cd string) string
- func (fb *FileBlocker) CheckContentType(contentType string) string
- func (fb *FileBlocker) CheckExt(ext string) string
- func (fb *FileBlocker) CheckPath(urlPath string) string
- func (fb *FileBlocker) ClearAll()
- func (fb *FileBlocker) Count() int
- func (fb *FileBlocker) List() []string
- func (fb *FileBlocker) Remove(ext string)
- func (fb *FileBlocker) ReplaceAll(exts []string)
- func (fb *FileBlocker) SetPath(p string)
- type FileExtProfile
- type FileProfileStore
- func (s *FileProfileStore) Create(name string, exts []string) (*FileExtProfile, error)
- func (s *FileProfileStore) Delete(id string) error
- func (s *FileProfileStore) GetByID(id string) *FileExtProfile
- func (s *FileProfileStore) GetByName(name string) *FileExtProfile
- func (s *FileProfileStore) List() []*FileExtProfile
- func (s *FileProfileStore) Load(path string) error
- func (s *FileProfileStore) NameByID(id string) (string, bool)
- func (s *FileProfileStore) ReplaceAll(profiles []FileExtProfile)
- func (s *FileProfileStore) Save() error
- func (s *FileProfileStore) SetPath(p string)
- func (s *FileProfileStore) Update(id, name string, exts []string) error
Constants ¶
This section is empty.
Variables ¶
var DefaultBlockedExts = []string{
".exe", ".dll", ".bat", ".cmd", ".ps1",
".vbs", ".scr", ".msi", ".pif", ".com",
}
DefaultBlockedExts is loaded at startup when no config override is provided. Covers common Windows malware/script delivery formats.
Functions ¶
func BlockConn ¶
func BlockConn(dst interface {
Write([]byte) (int, error)
Close() error
}, host, urlPath, ext, source string)
BlockConn writes a synthetic HTTP/1.1 403 response to a raw connection and closes it. Retained for the raw-conn callers and its own test; the SSL-inspect path no longer uses it (it emits via the protocol-neutral blockResponder so the same detector serves HTTP/2, where closing the shared conn on a per-stream block would kill sibling streams and Connection: close is illegal per RFC 9113 §8.2.2). The force-close here prevents HTTP/1.1 pipelined-request bypass.
func BlockMessage ¶ added in v1.0.70
BlockMessage returns the plain-text body of a file-block 403 for the given extension and source label. Exposed so the SSL-inspect path can emit the block through its protocol-neutral responder (HTTP/1.1 or HTTP/2) without this package owning HTTP framing; the legacy raw-conn BlockConn uses it too.
func ExtractCDFilename ¶
ExtractCDFilename extracts the filename from a Content-Disposition header. Returns "" if no filename is found. Used by per-rule file profile checking when the download URL doesn't contain the file extension (e.g. SourceForge's /files/latest/download pattern).
func LogBlock ¶ added in v1.0.70
func LogBlock(host, urlPath, ext, source string)
LogBlock emits the FILE_BLOCKED tunnel observability line. Split out of BlockConn so the SSL-inspect path can log identically while emitting the wire block through its own responder (keeping this package framing-free).
Types ¶
type FileBlocker ¶
type FileBlocker struct {
// contains filtered or unexported fields
}
FileBlocker holds the set of file extensions to block. Extensions are normalised to lowercase with a leading dot (e.g. ".exe"). All operations are safe for concurrent use. Persists to a JSON file when a path is configured via SetPath().
func NewBlocker ¶
func NewBlocker() *FileBlocker
NewBlocker returns an empty, ready-to-use FileBlocker. package main holds the process-wide singleton (var fileBlocker = fileblock.NewBlocker()).
func (*FileBlocker) Add ¶
func (fb *FileBlocker) Add(ext string)
Add inserts a normalised extension into the block list and persists.
func (*FileBlocker) CheckContentDisposition ¶
func (fb *FileBlocker) CheckContentDisposition(cd string) string
CheckContentDisposition returns the blocked extension if the Content-Disposition response header carries a filename with a blocked extension (catches downloads that use a generic URL but declare the real file name in the header).
func (*FileBlocker) CheckContentType ¶
func (fb *FileBlocker) CheckContentType(contentType string) string
CheckContentType returns the blocked extension if the response Content-Type header matches a dangerous MIME type whose associated extension is in the block list. This prevents bypass by renaming files (e.g. malware.exe → malware.txt). The contentType parameter should be the raw Content-Type header value (e.g. "application/x-msdownload; charset=utf-8").
func (*FileBlocker) CheckExt ¶
func (fb *FileBlocker) CheckExt(ext string) string
CheckExt checks if a specific extension is in the block list. Extension should include the dot (e.g., ".exe").
func (*FileBlocker) CheckPath ¶
func (fb *FileBlocker) CheckPath(urlPath string) string
CheckPath returns the blocked extension if urlPath ends with a blocked file extension, or empty string if the request is allowed. Pass r.URL.Path (not the full URL) to avoid matching query-string artefacts.
func (*FileBlocker) ClearAll ¶
func (fb *FileBlocker) ClearAll()
ClearAll removes all blocked extensions. Used by config import "replace" mode.
func (*FileBlocker) Count ¶
func (fb *FileBlocker) Count() int
Count returns the number of blocked extensions.
func (*FileBlocker) List ¶
func (fb *FileBlocker) List() []string
List returns a snapshot of the blocked extensions.
func (*FileBlocker) Remove ¶
func (fb *FileBlocker) Remove(ext string)
Remove deletes an extension from the block list and persists.
func (*FileBlocker) ReplaceAll ¶
func (fb *FileBlocker) ReplaceAll(exts []string)
ReplaceAll atomically replaces the in-memory extension set with the normalised contents of exts and persists once. Per-element semantics mirror Add exactly: lowercase + trim, leading-dot inserted if missing, empty / bare-dot entries skipped, duplicates collapsed by the set.
CL-13: used by applyConfigSnapshot's FileBlockExtensions branch to replace the prior ClearAll + per-extension Add loop, which triggered N+1 atomicWriteFile syscalls per snapshot apply (cap 10_000 extensions per maxSnapFileBlockExtensions). ReplaceAll triggers exactly one save() call regardless of N.
Save is invoked unconditionally — including when the new set is equal to the existing set. Skipping save on equal content would require deep-comparing maps under the lock and add a fast-path that could deviate from the long-term durability guarantee, which is out of CL-13 scope per the user brief.
func (*FileBlocker) SetPath ¶
func (fb *FileBlocker) SetPath(p string)
SetPath configures the persistence file and loads any previously saved extensions from it. If the file doesn't exist, the current in-memory state is kept (caller should load defaults before calling SetPath).
type FileExtProfile ¶
type FileExtProfile struct {
ID string `json:"id"`
Name string `json:"name"`
Extensions []string `json:"extensions"`
}
FileExtProfile is a named set of file extensions used for per-policy-rule blocking.
type FileProfileStore ¶
type FileProfileStore struct {
// contains filtered or unexported fields
}
FileProfileStore manages a persistent collection of file extension profiles. All operations are safe for concurrent use.
func (*FileProfileStore) Create ¶
func (s *FileProfileStore) Create(name string, exts []string) (*FileExtProfile, error)
Create adds a new profile. Returns an error if the name is already taken.
func (*FileProfileStore) Delete ¶
func (s *FileProfileStore) Delete(id string) error
Delete removes a profile by ID.
func (*FileProfileStore) GetByID ¶
func (s *FileProfileStore) GetByID(id string) *FileExtProfile
GetByID returns the profile with the given ID, or nil.
func (*FileProfileStore) GetByName ¶
func (s *FileProfileStore) GetByName(name string) *FileExtProfile
GetByName returns the profile with the given name (case-insensitive), or nil.
func (*FileProfileStore) List ¶
func (s *FileProfileStore) List() []*FileExtProfile
List returns a copy of all profiles.
func (*FileProfileStore) Load ¶
func (s *FileProfileStore) Load(path string) error
Load reads profiles from disk. If the file does not exist the built-in profiles are seeded and persisted so policy rules continue to work.
func (*FileProfileStore) NameByID ¶ added in v1.0.72
func (s *FileProfileStore) NameByID(id string) (string, bool)
NameByID returns the profile's name (a value copy, read under the lock) and whether it exists. Callers needing only the name must use this rather than GetByID().Name — GetByID returns the LIVE pointer, and reading .Name off it outside the lock races a concurrent Update (which mutates p.Name in place).
func (*FileProfileStore) ReplaceAll ¶
func (s *FileProfileStore) ReplaceAll(profiles []FileExtProfile)
ReplaceAll atomically replaces all profiles in memory and persists the new set (used by cluster config sync). Disk failure is logged; the in-memory swap is authoritative — the next CP heartbeat will re-attempt.
func (*FileProfileStore) Save ¶
func (s *FileProfileStore) Save() error
Save persists the current profile set to the configured path.
func (*FileProfileStore) SetPath ¶
func (s *FileProfileStore) SetPath(p string)
SetPath sets the persistence file path without reading from disk (use Load to also read existing profiles). Added for the package boundary: package main integration tests need to redirect persistence without the whitebox field access they relied on before the move.