Some checks failed
check / check (pull_request) Failing after 59s
Seven findings remain that cannot be fixed without either lying about the code or making a repo-wide naming decision, so each carries a per-site //nolint directive with its justification. gosec G115 (internal/log, internal/ui): term.IsTerminal takes an int and os.File.Fd() returns a uintptr, so the conversion is forced by the API. A file descriptor always fits in an int on every platform Go supports, and a closed file yields -1, which IsTerminal reports as not a terminal. gosec G703 (internal/vaultik/verify.go): the removed path comes from os.CreateTemp a few lines above and never from user input. G703's taint analysis treats every path derived from an *os.File as tainted, so there is no code shape that clears it. revive var-naming (internal/log, internal/crypto, internal/types): fixing these means renaming packages across the whole codebase, which is the repo owner's call, not a lint fix. Neither stdlib log nor stdlib crypto is imported anywhere in the repo, so nothing is actually shadowed today. The rename decision is tracked in issue #76. revive reports a package-name failure only once per package directory, on whichever file it happens to lint first, so every file of the affected packages carries the directive and lists nolintlint alongside revive so the ones that lose the race are not reported as unused. With this, make check exits 0 under the canonical .golangci.yml (sha256 021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb, unmodified), which also unblocks issue #59. TODO.md: record this work, correct the earlier entry that claimed make check was green when lint was still red, and move the next step on to the stale-branch triage.
230 lines
6.4 KiB
Go
230 lines
6.4 KiB
Go
// Package types provides custom types for better type safety across the
|
|
// vaultik codebase. Using distinct types for IDs, hashes, paths, and
|
|
// credentials prevents accidental mixing of semantically different values
|
|
// that happen to share the same underlying type.
|
|
package types //nolint:revive,nolintlint // rename decision tracked in #76
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// errCannotScan is returned when a database value cannot be scanned into
|
|
// an ID type.
|
|
var errCannotScan = errors.New("cannot scan value")
|
|
|
|
// FileID is a UUID identifying a file record in the database.
|
|
//
|
|
// used on values.
|
|
//
|
|
//nolint:recvcheck // Scan requires a pointer receiver; String/Value are
|
|
type FileID uuid.UUID
|
|
|
|
// NewFileID generates a new random FileID.
|
|
func NewFileID() FileID {
|
|
return FileID(uuid.New())
|
|
}
|
|
|
|
// ParseFileID parses a string into a FileID.
|
|
func ParseFileID(s string) (FileID, error) {
|
|
id, err := uuid.Parse(s)
|
|
if err != nil {
|
|
return FileID{}, err
|
|
}
|
|
|
|
return FileID(id), nil
|
|
}
|
|
|
|
// IsZero returns true if the FileID is the zero value.
|
|
func (id FileID) IsZero() bool {
|
|
return uuid.UUID(id) == uuid.Nil
|
|
}
|
|
|
|
// Value implements driver.Valuer for database serialization.
|
|
func (id FileID) Value() (driver.Value, error) {
|
|
return uuid.UUID(id).String(), nil
|
|
}
|
|
|
|
// Scan implements sql.Scanner for database deserialization.
|
|
func (id *FileID) Scan(src any) error {
|
|
if src == nil {
|
|
*id = FileID{}
|
|
|
|
return nil
|
|
}
|
|
|
|
var s string
|
|
|
|
switch v := src.(type) {
|
|
case string:
|
|
s = v
|
|
case []byte:
|
|
s = string(v)
|
|
default:
|
|
return fmt.Errorf("%w: %T into FileID", errCannotScan, src)
|
|
}
|
|
|
|
parsed, err := uuid.Parse(s)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid FileID: %w", err)
|
|
}
|
|
|
|
*id = FileID(parsed)
|
|
|
|
return nil
|
|
}
|
|
|
|
// BlobID is a UUID identifying a blob record in the database.
|
|
// This is distinct from BlobHash which is the content-addressed hash of the blob.
|
|
//
|
|
// used on values.
|
|
//
|
|
//nolint:recvcheck // Scan requires a pointer receiver; String/Value are
|
|
type BlobID uuid.UUID
|
|
|
|
// NewBlobID generates a new random BlobID.
|
|
func NewBlobID() BlobID {
|
|
return BlobID(uuid.New())
|
|
}
|
|
|
|
// ParseBlobID parses a string into a BlobID.
|
|
func ParseBlobID(s string) (BlobID, error) {
|
|
id, err := uuid.Parse(s)
|
|
if err != nil {
|
|
return BlobID{}, err
|
|
}
|
|
|
|
return BlobID(id), nil
|
|
}
|
|
|
|
// IsZero returns true if the BlobID is the zero value.
|
|
func (id BlobID) IsZero() bool {
|
|
return uuid.UUID(id) == uuid.Nil
|
|
}
|
|
|
|
// Value implements driver.Valuer for database serialization.
|
|
func (id BlobID) Value() (driver.Value, error) {
|
|
return uuid.UUID(id).String(), nil
|
|
}
|
|
|
|
// Scan implements sql.Scanner for database deserialization.
|
|
func (id *BlobID) Scan(src any) error {
|
|
if src == nil {
|
|
*id = BlobID{}
|
|
|
|
return nil
|
|
}
|
|
|
|
var s string
|
|
|
|
switch v := src.(type) {
|
|
case string:
|
|
s = v
|
|
case []byte:
|
|
s = string(v)
|
|
default:
|
|
return fmt.Errorf("%w: %T into BlobID", errCannotScan, src)
|
|
}
|
|
|
|
parsed, err := uuid.Parse(s)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid BlobID: %w", err)
|
|
}
|
|
|
|
*id = BlobID(parsed)
|
|
|
|
return nil
|
|
}
|
|
|
|
// SnapshotID identifies a snapshot, typically in format "hostname_name_timestamp".
|
|
type SnapshotID string
|
|
|
|
// ChunkHash is the SHA256 hash of a chunk's content.
|
|
// Used for content-addressing and deduplication of file chunks.
|
|
type ChunkHash string
|
|
|
|
// BlobHash is the SHA256 hash of a blob's compressed and encrypted content.
|
|
// This is used as the filename in S3 storage for content-addressed retrieval.
|
|
type BlobHash string
|
|
|
|
// FilePath represents an absolute path to a file or directory.
|
|
type FilePath string
|
|
|
|
// SourcePath represents the root directory from which files are backed up.
|
|
// Used during restore to strip the source prefix from paths.
|
|
type SourcePath string
|
|
|
|
// AgeRecipient is an age public key used for encryption.
|
|
// Format: age1... (Bech32-encoded X25519 public key)
|
|
type AgeRecipient string
|
|
|
|
// AgeSecretKey is an age private key used for decryption.
|
|
// Format: AGE-SECRET-KEY-... (Bech32-encoded X25519 private key)
|
|
// This type should never be logged or serialized in plaintext.
|
|
type AgeSecretKey string
|
|
|
|
// S3Endpoint is the URL of an S3-compatible storage endpoint.
|
|
type S3Endpoint string
|
|
|
|
// BucketName is the name of an S3 bucket.
|
|
type BucketName string
|
|
|
|
// S3Prefix is the path prefix within an S3 bucket.
|
|
type S3Prefix string
|
|
|
|
// AWSRegion is an AWS region identifier (e.g., "us-east-1").
|
|
type AWSRegion string
|
|
|
|
// AWSAccessKeyID is an AWS access key ID for authentication.
|
|
type AWSAccessKeyID string
|
|
|
|
// AWSSecretAccessKey is an AWS secret access key for authentication.
|
|
// This type should never be logged or serialized in plaintext.
|
|
type AWSSecretAccessKey string
|
|
|
|
// Hostname identifies a host machine.
|
|
type Hostname string
|
|
|
|
// Version is a semantic version string.
|
|
type Version string
|
|
|
|
// GitRevision is a git commit SHA.
|
|
type GitRevision string
|
|
|
|
// GlobPattern is a glob pattern for file matching (e.g., "*.log", "node_modules").
|
|
type GlobPattern string
|
|
|
|
// String methods for Stringer interface
|
|
|
|
func (id FileID) String() string { return uuid.UUID(id).String() }
|
|
func (id BlobID) String() string { return uuid.UUID(id).String() }
|
|
func (id SnapshotID) String() string { return string(id) }
|
|
func (h ChunkHash) String() string { return string(h) }
|
|
func (h BlobHash) String() string { return string(h) }
|
|
func (p FilePath) String() string { return string(p) }
|
|
func (p SourcePath) String() string { return string(p) }
|
|
func (r AgeRecipient) String() string { return string(r) }
|
|
func (e S3Endpoint) String() string { return string(e) }
|
|
func (b BucketName) String() string { return string(b) }
|
|
func (p S3Prefix) String() string { return string(p) }
|
|
func (r AWSRegion) String() string { return string(r) }
|
|
func (k AWSAccessKeyID) String() string { return string(k) }
|
|
func (h Hostname) String() string { return string(h) }
|
|
func (v Version) String() string { return string(v) }
|
|
func (r GitRevision) String() string { return string(r) }
|
|
func (p GlobPattern) String() string { return string(p) }
|
|
|
|
// Redacted String methods for sensitive types - prevents accidental logging
|
|
|
|
func (k AgeSecretKey) String() string { return "[REDACTED]" }
|
|
func (k AWSSecretAccessKey) String() string { return "[REDACTED]" }
|
|
|
|
// Raw returns the actual value for sensitive types when explicitly needed.
|
|
func (k AgeSecretKey) Raw() string { return string(k) }
|
|
|
|
// Raw returns the actual value for sensitive types when explicitly needed.
|
|
func (k AWSSecretAccessKey) Raw() string { return string(k) }
|