Documentation
¶
Overview ¶
HTTP abuse detection.
This file holds the access-log line parser, the per-scan aggregator struct (domlogStats), the UA classifier, the bot-classifier interface, and the static allowlist classifier that consults embedded bot IP ranges. The rDNS verifying classifier arrives in Task 5.
Package checks: http_asn_crawl detector — single-ASN distributed crawl of uncacheable URLs saturating one account's PHP pool. See docs/superpowers/specs/2026-06-24-http-asn-crawl-detector-design.md.
Index ¶
- Constants
- Variables
- func AFAlgMarkerPath() string
- func AFAlgOwner(ev AFAlgEvent) string
- func AccountFromContext(ctx context.Context) string
- func AccountHomePatterns() []string
- func AccountOwnerForDomain(domain string) (string, bool)
- func AllCheckNames() []string
- func ApplyAFAlgSeccompDropIns() ([]string, error)
- func AttributeSocketOwner(f *alert.Finding, uid uint32)
- func AuditHtaccessContent(path string, content []byte) ([]alert.Finding, []htaccessByteRange)
- func AuditHtaccessFile(path string) ([]alert.Finding, []htaccessByteRange)
- func AutoBlockIPs(cfg *config.Config, findings []alert.Finding) []alert.Finding
- func AutoBlockQueueStatuses(now time.Time) map[string]queuehealth.Status
- func AutoCleanHtaccess(cfg *config.Config, findings []alert.Finding) []alert.Finding
- func AutoFixPermissions(cfg *config.Config, findings []alert.Finding) (actions []alert.Finding, fixedKeys []string)
- func AutoFixWPCron(cfg *config.Config, findings []alert.Finding) (actions []alert.Finding, fixedKeys []string)
- func AutoKillProcesses(ctx context.Context, cfg *config.Config, findings []alert.Finding) []alert.Finding
- func AutoQuarantineFiles(cfg *config.Config, findings []alert.Finding) []alert.Finding
- func AutoRespondDBMalware(cfg *config.Config, findings []alert.Finding) []alert.Finding
- func AutoRespondDBMalwareWithPolicy(cfg *config.Config, findings []alert.Finding, ...) []alert.Finding
- func AutoVirtualPatchExposedFiles(cfg *config.Config, findings []alert.Finding) []alert.Finding
- func BumpDirectSMTPEgressFindings()
- func CMSCacheEmpty() bool
- func CMSCacheMayContainSize(size int64) bool
- func CanVerify(checkType string) bool
- func ChallengeRouteIPs(cfg *config.Config, findings []alert.Finding) []alert.Finding
- func ChallengeThenBlock(cfg *config.Config, findings []alert.Finding) (challengeActions, blockActions []alert.Finding)
- func CheckAFAlgEnforcement(_ context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckAFAlgSocketUsage(_ context.Context, _ *config.Config, st *state.Store) []alert.Finding
- func CheckAPIAuthFailures(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckAPITokens(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckAdminEmailOverlap(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckCpanelFileManager(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckCpanelLogins(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckCredentialReuse(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckCrontabs(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckDNSConnections(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckDNSZoneChanges(_ context.Context, _ *config.Config, store *state.Store) []alert.Finding
- func CheckDatabaseContent(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckDatabaseDumps(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckDatabaseObjects(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckDispatchQueueStatus(now time.Time) queuehealth.Status
- func CheckDrupalContent(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckEmailPasswords(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckErrorLogBloat(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckExecutionQueueStatus(now time.Time) queuehealth.Status
- func CheckExposedFiles(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckFTPLogins(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckFakeKernelThreads(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckFileIndex(ctx context.Context, cfg *config.Config, st *state.Store) []alert.Finding
- func CheckFilesystem(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckFirewall(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckForwarders(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckGroupWritablePHP(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckHealth(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckHtaccess(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckIPReputation(ctx context.Context, cfg *config.Config, scanState *state.Store) []alert.Finding
- func CheckJoomlaContent(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckKernelModules(ctx context.Context, _ *config.Config, store *state.Store) []alert.Finding
- func CheckLoadAverage(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckLocalThreatScore(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckMagentoContent(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckMailFilters(ctx context.Context, cfg *config.Config, st *state.Store) []alert.Finding
- func CheckMailPerAccount(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckMailQueue(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckModSecAuditLog(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckMySQLConfig(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckMySQLUsers(ctx context.Context, _ *config.Config, store *state.Store) []alert.Finding
- func CheckNulledPlugins(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckOpenBasedir(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckOpenCartContent(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckOutboundConnections(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckOutboundEmailContent(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckOutboundPasteSites(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckOutboundUserConnections(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckOutdatedPlugins(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckPHPConfigChanges(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckPHPContent(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckPHPHandler(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckPHPProcessLoad(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckPHPProcesses(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckPhishing(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckRPMIntegrity(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckRedisConfig(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckSSHDConfig(ctx context.Context, _ *config.Config, store *state.Store) []alert.Finding
- func CheckSSHKeys(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckSSHLogins(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckSSLCertIssuance(ctx context.Context, _ *config.Config, store *state.Store) []alert.Finding
- func CheckSensitiveFiles(_ context.Context, _ *config.Config, store *state.Store) []alert.Finding
- func CheckShadowChanges(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
- func CheckSupplyChain(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckSuspiciousProcesses(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckSwapAndOOM(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckSymlinkAttacks(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckUID0Accounts(ctx context.Context, _ *config.Config, _ *state.Store) []alert.Finding
- func CheckVulnerablePlugins(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckVulnerableTimThumb(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWAFStatus(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWHMAccess(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWPBruteForce(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWPConfig(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWPCore(ctx context.Context, cfg *config.Config, _ *state.Store) (findings []alert.Finding)
- func CheckWPCron(ctx context.Context, cfg *config.Config, scanState *state.Store) []alert.Finding
- func CheckWPPluginVerification(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWPTransientBloat(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWebmailLogins(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckWebshells(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
- func CheckYARADeep(ctx context.Context, cfg *config.Config, st *state.Store) []alert.Finding
- func CleanDatabaseSpam(account string) []alert.Finding
- func ContentDetectionVersion() string
- func ContextWithAccountScope(ctx context.Context, account string) context.Context
- func ContextWithScanOptions(ctx context.Context, opts AccountScanOptions) context.Context
- func CorrelationInputOf(f alert.Finding) (account string, eligible bool)
- func CurrentASNLookup() func(ip string) (asn uint, org string)
- func DerivedCorrelationChecks() []string
- func DiffSensitiveWatchset(prev, cur map[string]SensitiveFileState, contents map[string][]byte, ...) []alert.Finding
- func DirectSMTPEgressBackendEnabled(cfg *config.Config, backend string) bool
- func DisableFunctionsNeutralized(val string) bool
- func DisabledCheckConfigNames() []string
- func DisabledCheckNames() []string
- func EmailPasswordQueueStatuses(now time.Time) map[string]queuehealth.Status
- func EnumerateScanAccounts(_ *config.Config) ([]string, error)
- func EvaluateBadASNOutbound(cfg *config.Config, dstIP net.IP, asn uint, asOrg string) (alert.Finding, bool)
- func EvaluateConnection(cfg *config.Config, uid uint32, dstIP net.IP, dstPort uint16, localPort uint16, ...) (alert.Finding, bool)
- func EvaluateDirectSMTPEgress(cfg *config.Config, in DirectSMTPEgressInput) (alert.Finding, bool)
- func EvaluateExec(uid uint32, pid uint32, comm, exe, parentComm string) []alert.Finding
- func EvaluateSensitiveFileAppearance(path string) (alert.Finding, bool)
- func EvaluateSensitiveFileWrite(path string, uid, pid uint32, comm string) (alert.Finding, bool)
- func EvaluateSensitiveFileWriteSnapshot(path string, uid, pid uint32, comm string, content []byte, contentKnown bool) (alert.Finding, bool)
- func ExpandWatchset(root string) []string
- func ExtractIPFromFinding(f alert.Finding) string
- func FTPLoginFinding(line string, cfg *config.Config) (alert.Finding, bool)
- func FileContentSHA256(path string) string
- func FileIndexQueueStatuses(now time.Time) map[string]queuehealth.Status
- func FindingReverifyVersion() string
- func FixDescription(checkType, message string, filePath ...string) string
- func ForgetNetblockHistory(statePath, ip string, clear func()) error
- func FormatCleanResult(r CleanResult) string
- func FormatDBCleanResult(r DBCleanResult) string
- func FullScanMaxFileBytes(cfg *config.Config) int64
- func GetScanHomeDirs(ctx context.Context) ([]os.DirEntry, error)
- func HasFix(checkType string) bool
- func HashFile(path string) string
- func HostingAccountForUser(name string) string
- func IPResponseAnswersFinding(cfg *config.Config, f alert.Finding, blocked bool) bool
- func InitAutoBlockQueueHealth(statePath string) error
- func InlineQuarantine(f alert.Finding, path string, data []byte) (string, bool)
- func InlineQuarantineGated(cfg *config.Config, f alert.Finding, path string, data []byte) (string, bool)
- func InlineQuarantineGatedIdentified(cfg *config.Config, f *alert.Finding, path string, data []byte, ...) (string, bool, *alert.Finding)
- func InlineQuarantineIdentified(f alert.Finding, path string, data []byte, scanned os.FileInfo) (string, bool)
- func IsBenignPHPStub(path string) bool
- func IsBenignPHPStubBytes(buf []byte) bool
- func IsBenignPHPStubBytesComplete(buf []byte, complete bool) bool
- func IsCloudflareIP(ip net.IP) bool
- func IsContentReverifiable(check string) bool
- func IsDBObjectKind(s string) bool
- func IsDerivedCorrelationCheck(name string) bool
- func IsExternalDest(dest string, localDomains map[string]bool) bool
- func IsInfraIP(ip string, infraNets []string) bool
- func IsKnownForwarder(localPart, domain, dest string, knownForwarders []string) bool
- func IsPipeForwarder(dest string) bool
- func IsVerifiedCMSFile(path string) bool
- func IsVerifiedCMSHash(hash string) bool
- func IsWPTranslationCacheBytesComplete(buf []byte, complete bool) bool
- func IsWPVersionDataBytesComplete(buf []byte, complete bool) bool
- func LatestPurgeCheckNamesForReducedDeep() []string
- func LatestPurgeCheckNamesForTier(tier Tier) []string
- func LooksLikeCpanelRestoreStaging(path string) bool
- func LooksLikeWPOptimizeProbeByPath(path string) bool
- func LookupUID(account string) int
- func LookupUser(uid uint32) string
- func MailOwner(mailboxOrDomain string) string
- func MatchCrontabPatternsDeep(content string, cfg *config.Config) []string
- func MergeModSecUserConfSection(existing, srcData []byte) (merged []byte, changed bool)
- func MigrateWPCronCrontabs(cfg *config.Config) int
- func NextSensitiveDigests(prev map[string]SensitiveFileState, paths []string) (map[string]SensitiveFileState, map[string][]byte)
- func ObserveOperatorBlock(err error, source string)
- func PHPConfigRealtimeRootPatterns(cfg *config.Config) []string
- func PHPConfigSecurityBypasses(content string) []string
- func PHPTerminatesImmediately(buf []byte) bool
- func PHPTerminatesImmediatelyAt(buf []byte) (int, bool)
- func PathMatchesIgnore(path string, ignores []string) bool
- func PerfCheckNamesForTier(tier Tier) []string
- func PkgManagerRecentlyActive(now time.Time) bool
- func PluginInventoryQueueStatus(now time.Time) queuehealth.Status
- func PruneExemptAutoSubnets(cfg *config.Config, b IPBlocker) int
- func QuoteIdent(name string) (string, error)
- func RealtimeDocumentRootPatterns(cfg *config.Config) []string
- func RecordSelfWrite(path string, content []byte)
- func RecordUnattributedActiveSet(counts map[string]int)
- func RegisterDirectSMTPEgressMetrics(reg *metrics.Registry)
- func RemoveAFAlgSeccompDropIns() ([]string, error)
- func RemoveModSecUserConfSections(existing []byte) (cleaned []byte, changed bool)
- func ReportUnattributedCorrelation(counts map[string]int)
- func ReputationQueueStatus(now time.Time) queuehealth.Status
- func ResetAttributionHealthForTest()
- func ResolveWPCronRoots(cfg *config.Config) []string
- func ResolveWebRoots(cfg *config.Config) []string
- func RestoreVirtualPatchBackup(backupPath string, target *safepath.Target, meta QuarantineMeta) error
- func ReverifyStaleFindingsStats(ctx context.Context, store LatestFindingStore) ([]ContentReverifyDismissal, ReverifySweepStats, bool)
- func RunAccountScan(cfg *config.Config, store *state.Store, account string) []alert.Finding
- func RunAccountScanWithOptions(ctx context.Context, cfg *config.Config, store *state.Store, account string, ...) []alert.Finding
- func RunAll(cfg *config.Config, store *state.Store) ([]alert.Finding, []string)
- func RunAllDryRun(cfg *config.Config, store *state.Store) ([]alert.Finding, []string)
- func RunHardeningAudit(cfg *config.Config) *store.AuditReport
- func RunReducedDeep(cfg *config.Config, store *state.Store) ([]alert.Finding, []string)
- func RunReducedDeepWithContext(ctx context.Context, cfg *config.Config, store *state.Store) ([]alert.Finding, []string)
- func RunTier(cfg *config.Config, store *state.Store, tier Tier) ([]alert.Finding, []string)
- func RunTierDryRun(cfg *config.Config, store *state.Store, tier Tier) ([]alert.Finding, []string)
- func RunTierDryRunWithContext(ctx context.Context, cfg *config.Config, store *state.Store, tier Tier) ([]alert.Finding, []string)
- func RunTierWithContext(ctx context.Context, cfg *config.Config, store *state.Store, tier Tier) ([]alert.Finding, []string)
- func SSHAcceptedLoginFinding(line string, cfg *config.Config) (alert.Finding, bool)
- func SetASNLookup(fn func(ip string) (asn uint, org string))
- func SetAccountOwnerLookupForTest(fn func(domain string) (string, bool)) func()
- func SetBotVerifier(v *threatintel.AsyncBotVerifier, get func(net.IP, string) (bool, bool))
- func SetChallengeIPList(list ChallengeIPList)
- func SetCloudflareNets(cidrs []string)
- func SetCmdRunner(r CmdRunner)
- func SetGlobalThreatDBForTest(statePath string) func()
- func SetHostingAccountLookupForTest(fn func(name string) string) func()
- func SetIPBlocker(b IPBlocker)
- func SetOS(o OS)
- func SetPHPTaintAnalyzer(a PHPTaintAnalyzer)
- func SetSelfWriteStore(st *state.Store)
- func SetWebProbe(p webProbe)
- func ShouldCleanInsteadOfQuarantine(path string) bool
- func ShouldDemoteSeverity(f alert.Finding, res VerifyResult) bool
- func ShouldRestoreSeverity(f alert.Finding, res VerifyResult) bool
- func StampContentFingerprint(f *alert.Finding)
- func StoreLatestScanFindings(st *state.Store, purgeChecks []string, findings []alert.Finding)
- func StoreLatestScanFindingsWithCoverage(st *state.Store, purgeChecks []string, findings []alert.Finding, ...)
- func StoreLatestScanFindingsWithGaps(st *state.Store, purgeChecks []string, findings []alert.Finding, ...)
- func SwapUIDCacheForTest(path string) func()
- func VirtualPatchExposedFindings(_ *config.Config, findings []alert.Finding, apply bool) []alert.Finding
- func WPCoreQueueStatus(now time.Time) queuehealth.Status
- func WebRootPatterns(cfg *config.Config) []string
- func WithModSecReload(ctx context.Context, r *ModSecReloadReconciler) context.Context
- func WriteAFAlgMarker() error
- type AFAlgEvent
- type AFAlgKernelState
- type AccountScanOptions
- type ApplyBlockRequest
- type ApplyBlockResult
- type AttributionReport
- type AutoBlockFlushResult
- type CMSHashCache
- type ChallengeIPList
- type ChallengeRouteRecord
- type ChallengeUIStatsSnapshot
- type CheckDispatchProgress
- type CheckFunc
- type CheckInfo
- type CleanResult
- type CmdRunner
- type ContentReverifyDismissal
- type CorrelationClass
- type CorrelationResult
- type Correlator
- type CoverageGaps
- type DBCleanResult
- func DBCleanOption(account, optionName string, preview bool) DBCleanResult
- func DBDeleteSpam(account string, preview bool) DBCleanResult
- func DBDropObject(account, schema, kind, name string, preview bool) DBCleanResult
- func DBRevokeUser(account string, userID int, demote, preview bool) DBCleanResult
- func RestoreDBObjectBackup(backupKey string) DBCleanResult
- type DirectSMTPEgressInput
- type DispatchProgressSnapshot
- type EnforceAction
- type EnforceResult
- type IPBlocker
- type LatestFindingStore
- type ModSecReloadReconciler
- type OS
- type PHPExecutionOverlay
- type PHPTaintAnalyzer
- type QuarantineMeta
- type RDNSCache
- type RDNSCacheConfig
- type RemediationResult
- func ApplyFix(ctx context.Context, checkType, message, details string, filePath ...string) RemediationResult
- func CleanHtaccessFile(path string) RemediationResult
- func FixDisableWPCron(path string, opts WPCronFixOptions) RemediationResult
- func FixDisableWPCronInRoots(path string, allowedRoots []string, opts WPCronFixOptions) RemediationResult
- func FixDisplayErrorsOn(path string) RemediationResult
- func FixDisplayErrorsOnInRoots(path string, allowedRoots []string) RemediationResult
- func FixErrorLogBloat(path string) RemediationResult
- func FixErrorLogBloatInRoots(path string, allowedRoots []string) RemediationResult
- func QuarantineFindingFile(f alert.Finding) (RemediationResult, bool)
- func VirtualPatchExposedFile(filePath string) RemediationResult
- type ReverifySweepStats
- type SeccompCoverageSummary
- type SeccompUnitState
- type SensitiveFileState
- type ThreatDB
- func (db *ThreatDB) AddOperatorTemporary(ip, reason string, ttl time.Duration)
- func (db *ThreatDB) AddPermanent(ip, reason string)
- func (db *ThreatDB) AddTemporary(ip, reason string, ttl time.Duration)
- func (db *ThreatDB) AddWhitelist(ip string)
- func (db *ThreatDB) Count() int
- func (db *ThreatDB) FeedsStale() bool
- func (db *ThreatDB) IsConfigWhitelisted(ip string) bool
- func (db *ThreatDB) LastFeedRefresh() time.Time
- func (db *ThreatDB) Lookup(ip string) (string, bool)
- func (db *ThreatDB) LookupMatch(ip string) (ThreatMatch, bool)
- func (db *ThreatDB) PruneExpiredThreats() int
- func (db *ThreatDB) PruneExpiredWhitelist() int
- func (db *ThreatDB) RemovePermanent(ip string)
- func (db *ThreatDB) RemoveTemporary(ip string)
- func (db *ThreatDB) RemoveWhitelist(ip string)
- func (db *ThreatDB) SetConfigWhitelist(ips []string)
- func (db *ThreatDB) Stats() map[string]interface{}
- func (db *ThreatDB) TempWhitelist(ip string, ttl time.Duration)
- func (db *ThreatDB) UpdateFeeds() error
- func (db *ThreatDB) WhitelistedIPs() []WhitelistIP
- type ThreatMatch
- type Tier
- type ValiasEntry
- type VerifyInput
- type VerifyResult
- type WPCronFixOptions
- type WhitelistIP
Constants ¶
const ( BlockSourceScan = "scan" BlockSourceChallenge = "challenge" BlockSourceIncident = "incident" BlockSourceCentral = "central_intel" )
Block sources label who asked for an auto-response block. They feed the outcome metric and let evidence rows say which pipeline produced them.
const ( BlockSourceCLI = "cli" BlockSourceWebUI = "web_ui" )
Operator block sources, alongside the auto-response sources in applyblock.go. Operator paths bypass the chokepoint (they force-block and audit separately) but report into the same outcome metric.
const ( QuarantineRestoreReplaceIfUnchanged = "replace_if_unchanged" QuarantineRestoreRemoveIfUnchanged = "remove_if_unchanged" )
const ( CategoryAuth = "Authentication & Login" CategoryBruteForce = "Brute Force" CategoryMalware = "Malware & Webshells" CategoryWeb = "Web & Application" CategoryDatabase = "Database Content" CategoryEmail = "Email & Phishing" CategoryPerformance = "Performance" CategoryNetwork = "Network & Firewall" CategorySystem = "System Integrity" CategoryWAF = "WAF & ModSecurity" CategoryCorrelation = "Correlation & Health" CategoryInternal = "Internal" )
Category labels are the groupings shown in the multi-select UI. Keep the order below in sync with checkCategoryOrder so categories render in a sane order rather than alphabetically (Auth first, Internal last).
const ContentLogicVersion = 1
ContentLogicVersion identifies the current shape of the PHP content-analysis heuristic set (analyzePHPContent and its helpers). BUMP IT in the same commit as any change to those heuristics, so findings produced by the previous logic are re-verified and cleared by the daemon sweep. See docs/superpowers/specs/2026-06-20-stale-content-finding-reverification-design.md.
const ContentScannerVersion = 4
ContentScannerVersion identifies scanner behavior that is not represented by the loaded YAML signature version or YARA rule count. Bump it when shared content classification changes so the daemon re-checks stale findings.
const JSTaintLogicVersion = 2
JSTaintLogicVersion identifies the current semantics of the JavaScript keystroke taint analyzer (internal/jstaint). BUMP IT in the same commit as any change to its sources, propagation, sinks, resource limits, parser version, or content pre-filter so findings produced by the previous logic are re-verified under the new one.
const MaxInertPHPScanBytes = benignPHPStubMaxScan
MaxInertPHPScanBytes is the largest file the inert-content recognizers read in full. Translation caches and comment-only stubs need every byte to prove that no code follows; a proven PHP terminator can still be accepted from an incomplete prefix because its tail is unreachable.
const (
// PHPConfigMaxBytes is the shared scheduled and realtime read ceiling.
PHPConfigMaxBytes = 1 << 20
)
const PHPTaintLogicVersion = 4
PHPTaintLogicVersion identifies the current semantics of the PHP remote- source taint analyzer (internal/phptaint). BUMP IT in the same commit as any change to its parser, pre-filter, propagation, sinks, resource limits, or evidence semantics so existing findings are re-verified by the isolated worker under the new logic.
const SeccompDropInBaseName = "csm-copy-fail-seccomp.conf"
SeccompDropInBaseName is the file name CSM writes inside each unit's /etc/systemd/system/<unit>.d/ override directory. Stable so the hardening audit can scan for it and the remove path can clean up without guessing.
Variables ¶
var AncestryProbe func(pid uint32) bool
AncestryProbe reports whether the process tree rooted at pid contains a package-manager process. Nil on hosts without BPF process context, in which case ancestry checks no-op and the pkg-window + cron-content layers still apply. The BPF daemon wires this from processctx at startup.
var ErrKernelConfigUnreadable = os.ErrNotExist
EnsureFile is a sentinel return value: callers can use os.IsNotExist to test for the "kernel-config file absent" case explicitly.
var ErrModSecReloadNotConfigured = errors.New("modsec.reload_command is not set, so CSM cannot confirm its updated ModSecurity rules are active; they apply at the next web server reload")
ErrModSecReloadNotConfigured reports a CSM rule section that differs from the last one the web server reloaded, on a host with no reload command.
var ErrNoIPBlocker = errors.New("firewall engine not available")
ErrNoIPBlocker is returned when no firewall engine is wired. Callers must treat it as "the block did not happen", never as success.
var ErrVirtualPatchRestoreConflict = errors.New("virtual-patch restore conflicts with current .htaccess")
var ForceAll bool
ForceAll forces all checks to run regardless of throttle (used by baseline).
Functions ¶
func AFAlgMarkerPath ¶
func AFAlgMarkerPath() string
AFAlgMarkerPath returns the canonical marker file location. Exposed for the cmd/csm CLI which prints it to operators; production code in this package should reference the unexported constant directly.
func AFAlgOwner ¶
func AFAlgOwner(ev AFAlgEvent) string
AFAlgOwner resolves the hosting account whose process opened the socket. The audit uid is resolved through the shared passwd cache; root, service users and unknown uids yield "" so the finding stays unattributed.
func AccountFromContext ¶
AccountFromContext returns the account scope previously attached by ContextWithAccountScope, or "" when no scope is set.
func AccountHomePatterns ¶
func AccountHomePatterns() []string
AccountHomePatterns returns the glob for every account home ("<root>/*") on this platform. The realtime scanner needs it to recognise an account tree without hardcoding /home.
func AccountOwnerForDomain ¶
AccountOwnerForDomain resolves the hosting account that owns a mail domain, for producers that know a verified local mailbox or domain. Correlation never calls it; owners are resolved where the mailbox is known.
func AllCheckNames ¶
func AllCheckNames() []string
AllCheckNames returns every registered Check name, sorted alphabetically. Includes internal names; callers that render user-facing UI should use PublicCheckInfos instead.
func ApplyAFAlgSeccompDropIns ¶
ApplyAFAlgSeccompDropIns writes the canonical drop-in file for every candidate unit that exists on this host AND does not already have the file. After all writes, runs systemctl daemon-reload and a reload-or-restart per touched unit so the seccomp filter takes effect immediately.
Returns the list of units that received a new drop-in this call. An empty list with a nil error means everything was already covered (idempotent re-run).
func AttributeSocketOwner ¶
AttributeSocketOwner attaches the hosting account identified by a kernel socket or connection-event UID. A missing process snapshot does not remove this evidence. Root, service and unresolved UIDs remain unattributed. The message carries the account too, so dispatch and audit identities keep different accounts contacting the same destination separate.
func AuditHtaccessContent ¶
func AuditHtaccessFile ¶
AuditHtaccessFile runs every registered detector against the file at path. Returns the alert findings (one per detector hit) and the merged byte ranges that the cleaner would remove. The two outputs travel together so cleaning never disagrees with what the operator was alerted about.
func AutoBlockIPs ¶
AutoBlockIPs processes all findings, including repeats, for IP blocking.
func AutoBlockQueueStatuses ¶
func AutoBlockQueueStatuses(now time.Time) map[string]queuehealth.Status
AutoBlockQueueStatuses reads queue memory without waiting for the state mutex, filesystem, firewall or database. A batch is timed by operation progress.
func AutoCleanHtaccess ¶
AutoCleanHtaccess runs the hardened .htaccess cleaner against every finding emitted by the new detector registry, gated by AutoResponse.CleanHtaccess. Skipped when the daemon's auto-response pipeline is disabled overall.
Unlike AutoQuarantineFiles, this routes around the quarantine/clean fork (.htaccess files are infrastructure -- moving them to /opt/csm/quarantine breaks the site). Each invocation backs up the original to /opt/csm/quarantine/pre_clean/<ts>_* inside CleanHtaccessFile before atomic-replacing. Marks evaluated input findings so alert delivery cannot repeat a response.
func AutoFixPermissions ¶
func AutoFixPermissions(cfg *config.Config, findings []alert.Finding) (actions []alert.Finding, fixedKeys []string)
AutoFixPermissions sets world/group-writable PHP files to 0644. Returns the auto-response action findings and the keys of original findings that were successfully fixed (so the caller can dismiss them from the UI).
func AutoFixWPCron ¶
func AutoFixWPCron(cfg *config.Config, findings []alert.Finding) (actions []alert.Finding, fixedKeys []string)
AutoFixWPCron disables WP-Cron and installs a per-user system cron for every perf_wp_cron finding. Returns the auto-response action findings and the keys of the originals so the caller can dismiss them. Gated behind an explicit opt-in because it edits customer wp-config.php and crontabs.
func AutoKillProcesses ¶
func AutoKillProcesses(ctx context.Context, cfg *config.Config, findings []alert.Finding) []alert.Finding
AutoKillProcesses kills processes that match critical findings. Only targets: fake kernel threads, reverse shells, GSocket processes. Never kills root system services or cPanel processes.
func AutoQuarantineFiles ¶
AutoQuarantineFiles moves malicious files to quarantine directory. Preserves original path and metadata in a sidecar .meta file. Marks evaluated input findings so alert delivery cannot repeat a response.
func AutoRespondDBMalware ¶
AutoRespondDBMalware processes database injection findings and takes automated action: blocks attacker IPs extracted from WordPress session tokens, revokes compromised user sessions, and cleans confirmed malicious content from wp_options or stored database objects.
Only acts on high-confidence findings:
- db_options_injection with confirmed malicious external script URLs
- db_siteurl_hijack (siteurl/home pointing to malicious content)
- db_malicious_trigger/event/procedure/function with structured metadata
Does NOT act on:
- db_spam_injection (spam posts — needs manual review)
- db_post_injection (script in posts — too many FPs from page builders)
- db_options_injection without confirmed malicious URLs
func AutoRespondDBMalwareWithPolicy ¶
func AutoRespondDBMalwareWithPolicy(cfg *config.Config, findings []alert.Finding, canRemediate func(alert.Finding) bool) []alert.Finding
AutoRespondDBMalwareWithPolicy keeps session IP enforcement independent of permission to edit a database or revoke sessions. A nil policy permits both; callers with suppressions supply a per-finding remediation decision.
func AutoVirtualPatchExposedFiles ¶
AutoVirtualPatchExposedFiles is the scan-time entry point. It acts only when auto_response is enabled and the mode is "auto"; the write is gated by the shared auto_response dry_run flag (dry_run reports the intended denials).
func BumpDirectSMTPEgressFindings ¶
func BumpDirectSMTPEgressFindings()
BumpDirectSMTPEgressFindings increments the per-finding counter. Called by the connection consumer when EvaluateDirectSMTPEgress returns a finding.
func CMSCacheEmpty ¶
func CMSCacheEmpty() bool
CMSCacheEmpty reports whether any verified core files are cached, so a caller can skip hashing entirely when the answer cannot be yes.
func CMSCacheMayContainSize ¶
CMSCacheMayContainSize reports whether hashing a file of size bytes can possibly produce a cached CMS hash.
func CanVerify ¶
CanVerify reports whether VerifyFinding has an automated re-check for the given check type. The Web UI gates the per-finding "Re-check" action on this so it never shows a button that could only report "not auto-verifiable".
func ChallengeRouteIPs ¶
ChallengeRouteIPs processes findings and routes eligible IPs to the challenge list instead of hard-blocking them. Must be called BEFORE AutoBlockIPs so that challenged IPs are on the list when AutoBlockIPs checks Contains().
func ChallengeThenBlock ¶
func ChallengeThenBlock(cfg *config.Config, findings []alert.Finding) (challengeActions, blockActions []alert.Finding)
ChallengeThenBlock runs the two IP-disposition stages in their required order -- challenge routing first so an eligible IP is on the challenge list before AutoBlockIPs checks membership, then hard-blocking -- and returns both action sets. Auto-response call sites use this single helper instead of hand-ordering the two calls, so the "challenge before block" invariant cannot be silently broken by reordering in one path. Both stages run on the same finding set (the full/repeat-offender set); callers append the returned actions wherever their pipeline expects them.
func CheckAFAlgEnforcement ¶
CheckAFAlgEnforcement is the periodic critical-tier check that enforces the AF_ALG mitigation policy. When the operator has opted in (via `csm harden --copy-fail`, which writes the marker file), this check reverts any drift on each tick. It is a no-op in advisory mode.
Emits a Warning finding (one per tick that took action) so the operator has an alert-pipeline record that system state was modified. Steady-state ticks emit no findings. Warning is the lowest severity available in the alert.Severity enum (Warning < High < Critical, no Info level).
func CheckAFAlgSocketUsage ¶
CheckAFAlgSocketUsage scans the audit log for csm_af_alg_socket events and emits one Critical finding per strictly-newer event. The first run alerts on every event found — AF_ALG-from-userland is an exploit signature for CVE-2026-31431 ("Copy Fail"), not a baseline metric, so silent seeding would hide pre-existing compromise. The cursor in state.Store prevents duplicates on subsequent sweeps and survives daemon restarts.
Filtering is delegated to grep so we don't load the whole multi-hundred-MB audit log into memory each tick (same precedent as getAuditShadowInfo in auth.go). RunAllowNonZero is required because grep returns exit 1 on "no match" — the healthy default — and that must not surface as an error.
func CheckAPIAuthFailures ¶
CheckAPIAuthFailures parses cPanel access log for failed API authentication.
func CheckAPITokens ¶
func CheckAdminEmailOverlap ¶
func CheckAdminEmailOverlap(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckAdminEmailOverlap records every WordPress administrator email encountered during an account scan into a server-wide bbolt bucket, then emits a Warning finding for each email whose owner list now spans the configured minimum number of distinct accounts. The detection surface is shared-hosting credential leakage: a single compromised contractor account is one credential disclosure away from administrator access on every site they touch.
The check is silent when the bbolt store is unavailable (early daemon startup, test harness without state injection) -- it can't observe overlap without persistence between scans, and falling silent is better than a misleading partial result.
func CheckCpanelFileManager ¶
func CheckCpanelFileManager(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckCpanelFileManager parses the cPanel access log for file management operations from non-infra IPs (file uploads, edits via cPanel File Manager).
func CheckCpanelLogins ¶
CheckCpanelLogins parses the cPanel session log for suspicious login activity: - cPanel (cpaneld) logins from non-infra IPs - Same account logged in from multiple distinct IPs (credential compromise indicator) - Password change purge events (attacker or auto-response password resets)
func CheckCredentialReuse ¶
CheckCredentialReuse flags WordPress administrator accounts that share an identical password hash across two or more distinct hosting accounts. Password hashes are salted on modern WordPress installs, so this is an exact at-rest hash reuse signal, not a weak-password detector.
Privacy: the raw password hash is never stored, logged, or emitted. Only a truncated one-way fingerprint is used to group identical hashes, and findings report the affected accounts and a count -- not the hash.
func CheckCrontabs ¶
func CheckDNSConnections ¶
CheckDNSConnections looks for established connections to port 53 on DNS servers that are NOT in /etc/resolv.conf. This catches DNS tunneling, GSocket relay discovery, and malware using hardcoded resolvers. Connections owned by known DNS server processes (e.g. named) are skipped.
func CheckDNSZoneChanges ¶
CheckDNSZoneChanges monitors named zone files for tampering.
A raw file-hash watch over /var/named is too coarse: every cPanel serial bump, AutoSSL DCV TXT record, DKIM rotation, and customer Zone Editor edit rewrites the file, so hashing the whole zone alerts on routine activity and buries real hijacks. This check instead compares only the security-relevant records and weighs the change against cPanel provenance:
- The "security fingerprint" covers delegation (NS), mail (MX), and apex/ wildcard address records -- the records an attacker rewrites to take over a domain. Serial, TXT/DKIM/SPF/DCV, and ordinary subdomain A records are ignored, so legitimate churn stays quiet.
- cPanel stamps each zone it writes with an "(update_time):" header. A security change with no advance of that stamp means the file was edited out of band (direct file write, or a non-cPanel path) -- the signature of a hijack -- and is reported High. A security change that did go through cPanel is trusted more: an NS/MX move still surfaces as a Warning (could be a compromised account), while an apex/wildcard address repoint by the authenticated owner is routine and stays quiet.
There is deliberately no bulk-suppression gate: a mass out-of-band NS rewrite across every hosted domain is exactly the incident operators must see, and the per-record/provenance model already keeps benign mass operations quiet.
func CheckDatabaseContent ¶
CheckDatabaseContent scans WordPress databases for injected malware, spam content, siteurl hijacking, and rogue admin accounts.
func CheckDatabaseDumps ¶
CheckDatabaseDumps detects mysqldump/pg_dump processes running under non-root users - potential data exfiltration.
func CheckDatabaseObjects ¶
CheckDatabaseObjects scans every WordPress installation's database for triggers, events, procedures, and functions. Critical findings fire when the body matches a known-malware pattern; Warning findings fire when an object exists at all (vanilla CMSes ship none). The Detection.DBObjectScanning kill-switch silences both emit paths without disabling the manual drop-object CLI.
func CheckDispatchQueueStatus ¶
func CheckDispatchQueueStatus(now time.Time) queuehealth.Status
CheckDispatchQueueStatus measures pending checks and their runner wrappers.
func CheckDrupalContent ¶
func CheckEmailPasswords ¶
CheckEmailPasswords audits Dovecot email account passwords for weak/predictable patterns. Uses internal throttle: skips if last refresh was less than password_check_interval_min ago.
func CheckErrorLogBloat ¶
CheckErrorLogBloat walks configured web roots (default /home/*/public_html on cPanel) looking for error_log files that exceed the configured size threshold. The runner enforces a 60-minute throttle via checkThrottleMin.
func CheckExecutionQueueStatus ¶
func CheckExecutionQueueStatus(now time.Time) queuehealth.Status
CheckExecutionQueueStatus reads memory only, including after a runner exits.
func CheckExposedFiles ¶
CheckExposedFiles scans every cPanel docroot for sensitive files (database dumps, backup archives, config/source backups, phpinfo) that the web server actually serves, and reports each confirmed exposure. It reads only response headers except for a bounded phpinfo body used to reject empty stubs.
func CheckFTPLogins ¶
CheckFTPLogins detects pure-ftpd brute force. With a state store it reads /var/log/messages forward-only and accumulates per-IP failures over a sliding window; without a store it falls back to the legacy per-cycle tail.
func CheckFakeKernelThreads ¶
func CheckFileIndex ¶
CheckFileIndex builds an index of suspicious files using pure Go directory reads, diffs against the previous index, and alerts on new files. Uses directory mtime caching: unchanged dirs carry forward previous entries without calling ReadDir, while changed dirs are re-scanned.
When ctx carries AccountScanOptions with ForceFileIndex=true the function runs in audit mode: it enumerates only the in-scope account, bypasses the directory mtime cache entirely, and writes none of the live state files (fileindex.current, fileindex.previous, dircache.json). The normal incremental baseline is left byte-for-byte intact. All indexed files are treated as new so the caller receives findings for the full current state of the account.
func CheckFilesystem ¶
CheckFilesystem uses globs and targeted ReadDir to check for backdoors, hidden files, and SUID binaries. No `find` command needed.
func CheckFirewall ¶
func CheckForwarders ¶
CheckForwarders audits all valiases and vfilters files for dangerous forwarder patterns. Uses internal throttle: skips if last refresh was less than password_check_interval_min ago (reuses the same interval).
func CheckGroupWritablePHP ¶
CheckGroupWritablePHP scans for PHP files that are group-writable where the group is the web server (nobody/www-data). This allows webshells to persist by the web server modifying PHP files.
func CheckHealth ¶
CheckHealth verifies that CSM's dependencies are working. Reports on missing external commands, broken auditd, etc.
func CheckHtaccess ¶
CheckHtaccess scans for malicious .htaccess directives using pure Go ReadDir.
func CheckIPReputation ¶
func CheckIPReputation(ctx context.Context, cfg *config.Config, scanState *state.Store) []alert.Finding
CheckIPReputation looks up non-infra IPs against threat intelligence. Four-tier approach:
- Skip if already blocked
- Check local threat DB (permanent blocklist + free feeds)
- Check AbuseIPDB cache
- Query AbuseIPDB for truly unknown IPs (max 5/cycle, ~720/day)
func CheckJoomlaContent ¶
func CheckJoomlaContent(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
CheckJoomlaContent scans every Joomla installation under /home/*/public_html for malware-pattern matches in the three canonical attacker-touched tables. Mirrors the structure of CheckDatabaseContent without sharing code -- the credentials and table layout differ enough that a generic dispatcher is more abstraction than this point in the codebase needs.
func CheckKernelModules ¶
CheckKernelModules compares loaded kernel modules against baseline. All modules present at baseline time are considered known. Only modules loaded AFTER baseline trigger alerts.
func CheckLoadAverage ¶
CheckLoadAverage compares load averages against per-core thresholds from config. The 1-minute load drives the Critical / High findings; when 1-minute is below the High threshold we additionally check the 5- and 15-minute averages for sustained pressure (>= 0.7 * High threshold on both) and emit a Warning. The sustained variant catches "constant 22%-of-cores busy for 15 minutes" which is invisible to a 1-minute spike check but is what operators actually want to see.
func CheckLocalThreatScore ¶
CheckLocalThreatScore generates findings for IPs that have accumulated a high local threat score but have not yet been blocked. Runs every 10 minutes as part of TierCritical.
func CheckMagentoContent ¶
func CheckMailFilters ¶
CheckMailFilters scans per-mailbox and domain-wide Exim filters for BEC-style exfiltration rules. Throttled to PasswordCheckIntervalMin, like the forwarder audit it complements.
func CheckMailPerAccount ¶
CheckMailPerAccount counts recent Exim arrivals per envelope-sender domain. Ownership requires the same verified submitter across the entire count.
func CheckMailQueue ¶
func CheckModSecAuditLog ¶
func CheckModSecAuditLog(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
CheckModSecAuditLog parses the ModSecurity audit log for blocked attacks. High-volume attackers are reported for potential auto-blocking.
func CheckMySQLConfig ¶
CheckMySQLConfig inspects MySQL global variables and runtime status for performance-impacting misconfigurations. Each issue emits its own finding with a stable message so deduplication works correctly.
func CheckMySQLUsers ¶
CheckMySQLUsers queries for MySQL users with elevated privileges that aren't standard cPanel-managed users.
func CheckNulledPlugins ¶
CheckNulledPlugins scans WordPress plugin directories for signs of nulled/pirated plugins: missing licenses, known crack patterns, GPL bypass code, and plugins not found on wordpress.org.
func CheckOpenBasedir ¶
CheckOpenBasedir verifies that each cPanel account has proper PHP isolation via CageFS and/or open_basedir. Flags accounts where CageFS is disabled AND open_basedir is not set.
func CheckOpenCartContent ¶
func CheckOutboundEmailContent ¶
func CheckOutboundEmailContent(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckOutboundEmailContent samples outbound email content from Exim spool for phishing URLs, credential harvesting language, suspicious mailers, and Reply-To mismatches.
func CheckOutboundPasteSites ¶
func CheckOutboundPasteSites(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckOutboundPasteSites detects connections to known paste/exfiltration sites.
func CheckOutboundUserConnections ¶
func CheckOutboundUserConnections(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckOutboundUserConnections looks for non-root user processes making outbound connections to IPs that aren't infra or well-known services. Catches compromised accounts phoning home.
func CheckOutdatedPlugins ¶
CheckOutdatedPlugins scans all WordPress installations for plugins with available updates and emits findings based on severity of the version gap. Results are cached in bbolt with a configurable refresh interval (default 24h).
func CheckPHPConfigChanges ¶
func CheckPHPConfigChanges(ctx context.Context, cfg *config.Config, store *state.Store) []alert.Finding
CheckPHPConfigChanges monitors .user.ini and php.ini files anywhere under an account's document roots for settings that weaken PHP security (disable_functions cleared or neutralized, allow_url_include enabled, open_basedir removed). It runs as a deep check; the fanotify watcher also catches these writes in real-time.
func CheckPHPContent ¶
func CheckPHPHandler ¶
CheckPHPHandler detects PHP CGI handler usage on LiteSpeed servers. On LiteSpeed, CGI is significantly slower than LSAPI; this check fires a Critical finding for each PHP version using the CGI handler.
func CheckPHPProcessLoad ¶
CheckPHPProcessLoad scans /proc for PHP web workers, groups them by user, and fires Critical if total exceeds cores*multiplier, High per user if individual count exceeds threshold.
func CheckPHPProcesses ¶
CheckPHPProcesses inspects running lsphp processes to detect active webshell execution. Only reads /proc cmdline - zero disk I/O.
func CheckPhishing ¶
CheckPhishing scans HTML files in user document roots for phishing pages. Uses three detection layers:
- Content analysis - brand impersonation + credential harvesting patterns
- Structural analysis - self-contained HTML with embedded assets
- Directory anomaly - lone HTML files in otherwise empty directories
func CheckRPMIntegrity ¶
CheckRPMIntegrity verifies critical system binaries haven't been modified. Only checks a small set of security-critical packages. Dispatches to rpm -V on RHEL-family systems and debsums/dpkg --verify on Debian family.
func CheckRedisConfig ¶
CheckRedisConfig inspects a local Redis instance for performance-impacting misconfigurations: unset maxmemory, noeviction policy, non-expiring keys, and an overly aggressive bgsave schedule for the dataset size.
func CheckSSHDConfig ¶
CheckSSHDConfig monitors sshd_config for dangerous changes.
func CheckSSHKeys ¶
func CheckSSHLogins ¶
CheckSSHLogins parses the platform authentication log for SSH logins from non-infra IPs. With a state store it reads the log forward-only from where the previous cycle stopped; without one it falls back to a per-cycle tail.
func CheckSSLCertIssuance ¶
func CheckSSLCertIssuance(ctx context.Context, _ *config.Config, store *state.Store) []alert.Finding
CheckSSLCertIssuance monitors AutoSSL logs for new certificate issuance. Attackers may issue certificates for phishing domains using compromised accounts.
func CheckSensitiveFiles ¶
CheckSensitiveFiles is the periodic safety-net that runs when the BPF live monitor is unavailable or disabled. It content-hashes every watchset path and emits a finding when a hash differs from the previous run. The first run records baselines without emitting findings.
CheckShadowChanges in auth.go does richer per-user diff and infra-IP suppression for /etc/shadow specifically; this catch-all complements that for sshd_config, sudoers, cron drop-ins, etc. Both run in parallel; audit-log dedup handles the (rare) overlap.
func CheckShadowChanges ¶
func CheckSupplyChain ¶
CheckSupplyChain scans customer dependency lockfiles for versions with known advisories. Dormant unless an advisory database is present at <state>/advisories/supply-chain.json.
func CheckSwapAndOOM ¶
CheckSwapAndOOM checks for OOM killer events in dmesg and elevated swap usage from /proc/meminfo. Host OOM is Critical, cgroup OOM Warning, and swap usage above 50% High.
func CheckSymlinkAttacks ¶
CheckSymlinkAttacks detects symbolic links inside user public_html directories that point outside the account's own directory. This is a classic shared hosting attack to read other users' files.
func CheckUID0Accounts ¶
func CheckVulnerablePlugins ¶
func CheckVulnerablePlugins(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckVulnerablePlugins matches the shared WordPress plugin inventory against the curated known-vulnerable feed. It participates in the same serialized refresh as CheckOutdatedPlugins and only reports -- it never disables a plugin.
func CheckVulnerableTimThumb ¶
func CheckVulnerableTimThumb(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckVulnerableTimThumb scans web document roots for bundled TimThumb scripts and reports each confirmed instance. Detection-only: TimThumb is legitimate (if abandoned) code, so it is never auto-quarantined -- removing it would break the theme.
func CheckWAFStatus ¶
CheckWAFStatus verifies that ModSecurity is loaded, the engine is in enforcement mode (not DetectionOnly), OWASP/Comodo rules are active, and rules are up to date.
func CheckWHMAccess ¶
CheckWHMAccess parses the cPanel access log for WHM (port 2087) logins and password change API calls from non-infra IPs. Only reads the tail of the log - lightweight.
func CheckWPBruteForce ¶
CheckWPBruteForce detects brute force attacks against wp-login.php and xmlrpc.php by scanning access logs. Always scans per-domain domlogs because on LiteSpeed+cPanel, virtual host traffic only appears there. The central access log is scanned as a supplement.
Aggregates per-IP counts across ALL domains -- catches attackers who distribute requests across many sites to stay under per-site thresholds.
func CheckWPConfig ¶
CheckWPConfig scans /home/*/public_html (max depth 2) for wp-config.php files and reports excessive WP_MEMORY_LIMIT values, unlimited max_execution_time, and display_errors enabled in production. The runner enforces a 60-minute throttle via checkThrottleMin.
func CheckWPCore ¶
func CheckWPCore(ctx context.Context, cfg *config.Config, _ *state.Store) (findings []alert.Finding)
CheckWPCore runs wp core verify-checksums for each WordPress installation using a bounded worker pool for concurrency. Installations that pass verification have their core files cached in GlobalCMSCache so the real-time scanner can skip signature matches on known-clean CMS files.
func CheckWPCron ¶
CheckWPCron scans configured web roots plus validated cPanel document roots for WordPress installs that have not disabled the built-in WP-Cron mechanism. Running WP-Cron via HTTP is a common cause of high load on busy sites. The runner enforces a 60-minute throttle via checkThrottleMin.
func CheckWPPluginVerification ¶
func CheckWPPluginVerification(ctx context.Context, cfg *config.Config, _ *state.Store) []alert.Finding
CheckWPPluginVerification reports the shared inventory independently of its outdated/known-vulnerable consumers. The existing plugin refresh interval and disabled_checks controls remain authoritative.
func CheckWPTransientBloat ¶
CheckWPTransientBloat scans configured web roots (default /home/*/public_html on cPanel) for WordPress installs and queries each database for oversized transients. DB credentials are read from wp-config.php; the password is passed via MYSQL_PWD environment variable (never on the command line). The runner enforces a 60-minute throttle via checkThrottleMin.
func CheckWebmailLogins ¶
CheckWebmailLogins parses cPanel access log for webmail logins from non-infra IPs.
func CheckWebshells ¶
CheckWebshells uses pure Go ReadDir to scan for known webshell files and directories. No `find` command needed.
func CheckYARADeep ¶
CheckYARADeep is the shared rolling deep-content walk. It reads each file once and dispatches the same in-memory snapshot to three consumers with independent cursors and completion records: the YARA backend and the JS and PHP taint analyzers. A missing, disabled, or failed consumer neither prevents nor controls another consumer's progress.
func CleanDatabaseSpam ¶
CleanDatabaseSpam removes known spam/malware patterns from WordPress database content. Targets wp_posts and wp_options tables. Returns findings for each cleaned row.
func ContentDetectionVersion ¶
func ContentDetectionVersion() string
ContentDetectionVersion returns a token identifying the full content-detection logic in effect: the heuristic and scanner versions, the loaded signature-set version, the loaded YARA rule count, and both taint analyzer versions. The re-verifier always re-runs the real classifier, so this token only gates the daemon sweep and enriches audit detail; its precision is not security-critical.
func ContextWithAccountScope ¶
ContextWithAccountScope returns a derived context that restricts filesystem-based checks to a single cPanel/Linux account. Callers pass the resulting context into every check; helpers like GetScanHomeDirs read the scope back out. Empty account is a no-op (returns ctx unchanged, equivalent to a full host scan).
func ContextWithScanOptions ¶
func ContextWithScanOptions(ctx context.Context, opts AccountScanOptions) context.Context
ContextWithScanOptions attaches opts to ctx so that helpers called during a scan can read the active options without threading a parameter through every call site. Use ScanOptionsFromContext to retrieve them.
func CorrelationInputOf ¶
CorrelationInputOf reports how one finding enters cross-account correlation: the hosting account it resolves to, empty when none could be determined, and whether its check is an eligible input at all. It applies the same registry classification and identity rules CorrelateFindings uses, so a caller can explain or calibrate a correlation result without re-implementing them. Eligibility here is the check's class only; whether a given finding then counts also depends on its severity.
func CurrentASNLookup ¶
CurrentASNLookup returns the wired ASN resolver, or nil when none is set. Both the polling connection scan and the live BPF connection evaluator use it so bad-ASN classification behaves identically on either path.
func DerivedCorrelationChecks ¶
func DerivedCorrelationChecks() []string
DerivedCorrelationChecks returns the names correlation itself emits, sorted. The caller owns the slice.
func DiffSensitiveWatchset ¶
func DiffSensitiveWatchset(prev, cur map[string]SensitiveFileState, contents map[string][]byte, liveReported map[string]SensitiveFileState) []alert.Finding
DiffSensitiveWatchset compares two refresh snapshots of the watchset and returns the findings the newer one warrants. Both maps are keyed by absolute path. contents holds the bytes used for cur's content digests.
Identity is the path, never the inode. Keying on dev+inode reported every atomic rewrite (write temp, rename over) as a brand-new file, which is how /etc/passwd came to "appear" 16 times in a month on a live host. A path the previous snapshot knew about can only have changed, not appeared.
Paths that vanished produce nothing: the caller unwatches the inode, and a deletion is not evidence of the tampering this watchset exists to catch.
liveReported holds the exact states whose live-hook findings were delivered since the last refresh. A matching state is adopted without a duplicate. An appearance is still reported: the hook describes a write, not the fact that the path did not exist before.
func DisableFunctionsNeutralized ¶
DisableFunctionsNeutralized reports whether a disable_functions value fails to actually disable any dangerous function -- empty, "none", or set to junk (the `disable_functions=ByPassed By 0xNix` camouflage). A genuine hardening list names at least one exact dangerous function; substrings such as "systemd" do not disable system().
func DisabledCheckConfigNames ¶
func DisabledCheckConfigNames() []string
DisabledCheckConfigNames returns every value top-level disabled_checks accepts: this is exactly the set splitDisabledChecks honors -- every emitted finding name (including internal ones) plus every compatibility runner ID. It is broader than DisabledCheckNames (the UI vocabulary) so POST-side validation never rejects a value an existing operator config relies on.
func DisabledCheckNames ¶
func DisabledCheckNames() []string
DisabledCheckNames returns the sorted public finding-name vocabulary accepted by top-level disabled_checks for scheduled check execution. Runner IDs are also accepted by splitDisabledChecks for existing configs, but are not exposed in the UI.
func EmailPasswordQueueStatuses ¶
func EmailPasswordQueueStatuses(now time.Time) map[string]queuehealth.Status
EmailPasswordQueueStatuses reads admission and KDF ownership from memory.
func EnumerateScanAccounts ¶
EnumerateScanAccounts returns the sorted list of cPanel account usernames eligible for a server-wide scan. Source of truth: the cPanel user registry (/var/cpanel/users) intersected with present home directories (/home/<user>). All filesystem access goes through the package osFS hook so it is fakeable.
Fallback: when /var/cpanel/users is absent (non-cPanel platform), the function falls back to enumerating /home subdirectories whose names pass name validation. This makes the function usable on generic Linux hosts.
A hard FS error reading the registry (not os.ErrNotExist) is propagated as an error. An empty registry returns ([]string{}, nil).
func EvaluateBadASNOutbound ¶
func EvaluateBadASNOutbound(cfg *config.Config, dstIP net.IP, asn uint, asOrg string) (alert.Finding, bool)
EvaluateBadASNOutbound classifies one outbound connection's destination by autonomous system and returns a finding when the ASN is bad. It is a pure function: the caller supplies the destination IP and the ASN/org already resolved from the GeoLite2-ASN database, so the classifier has no IO and is the third leg of the host-takeover chain correlator.
Classification:
- blocked_asns always flags (e.g. known bulletproof hosters);
- when allowed_asns is non-empty, any ASN outside it flags (allowlist mode for hosts whose legitimate egress is confined to a few providers).
An ASN of 0 (no AS found for the IP) is skipped: classifying it would flag every destination missing from the ASN database. Private, loopback, link-local, and unspecified destinations are skipped because ASN lookup is meaningless for them.
func EvaluateConnection ¶
func EvaluateConnection( cfg *config.Config, uid uint32, dstIP net.IP, dstPort uint16, localPort uint16, proto string, user string, ) (alert.Finding, bool)
EvaluateConnection returns a populated alert.Finding and true when the connection should be reported, or a zero finding and false when it should be ignored. Host-interface lookups are cached. Used by the BPF live backend (per-event) and the polling backend (per row of /proc/net/tcp[6]).
func EvaluateDirectSMTPEgress ¶
EvaluateDirectSMTPEgress returns a populated finding when the input represents a non-MTA local process opening an outbound SMTP connection. Owner resolution uses the shared passwd cache. Detector-disabled config returns (zero, false) without inspecting the input.
func EvaluateExec ¶
EvaluateExec returns findings for a single execve event observed by the BPF live backend. Inputs are the (UID, PID, comm, exe, parentComm) tuple the kernel hook collects. It stamps the detection time because these findings enter the realtime bus directly. The legacy periodic checks (CheckSuspiciousProcesses, CheckFakeKernelThreads) keep using cmdline-aware detection that this function cannot replicate.
func EvaluateSensitiveFileAppearance ¶
EvaluateSensitiveFileAppearance returns a finding when a path no previous watchset refresh had seen shows up -- a genuinely new cron drop-in, sudoers fragment, or user crontab.
func EvaluateSensitiveFileWrite ¶
EvaluateSensitiveFileWrite returns a populated alert.Finding and true when the BPF live backend observed a write to a watchset path. It reads current content for content-bound self-write suppression. Returns false for paths classifySensitive does not recognise -- the BPF program already filters via its dev+inode map, but this guards against stale map entries pointing at unrelated files.
func EvaluateSensitiveFileWriteSnapshot ¶
func EvaluateSensitiveFileWriteSnapshot(path string, uid, pid uint32, comm string, content []byte, contentKnown bool) (alert.Finding, bool)
EvaluateSensitiveFileWriteSnapshot evaluates a live write against bytes captured with the file state that will be recorded for refresh deduplication. Keeping those two observations together prevents a concurrent rename from making the finding describe or suppress different content.
func ExpandWatchset ¶
ExpandWatchset returns the absolute paths in the watchset, with globs expanded against the given filesystem root. Non-existent paths drop silently; the next refresh picks them up once they are created. root is "/" in production and a t.TempDir in tests.
func ExtractIPFromFinding ¶
ExtractIPFromFinding extracts an IP address from a finding.
func FTPLoginFinding ¶
FTPLoginFinding builds the finding for a successful pure-ftpd login line, reporting false when the line is not a login or the client address is loopback, infrastructure, or unusable. The daemon's realtime log watcher calls it so a login seen live and the same line re-read by CheckFTPLogins carry one identity; without that the state store sees two findings and the operator gets one login reported twice. It has no brute-force history, so the escalation to ftp_login_after_bruteforce stays with the scheduled check.
func FileContentSHA256 ¶
FileContentSHA256 returns the hex SHA-256 of the whole file, or "" if the file cannot be read, is not a regular file, or exceeds the size cap.
func FileIndexQueueStatuses ¶
func FileIndexQueueStatuses(now time.Time) map[string]queuehealth.Status
FileIndexQueueStatuses reads memory without waiting for a scan or filesystem.
func FindingReverifyVersion ¶
func FindingReverifyVersion() string
FindingReverifyVersion identifies every verifier family the startup sweep runs unattended, plus the sweep's own semantics. Including the exposure verifier forces one sweep when that family is first deployed or its fail-closed semantics change.
func FixDescription ¶
FixDescription returns a human-readable description of what the fix will do for a given check type and file path. Returns empty string if no fix is available.
func ForgetNetblockHistory ¶
ForgetNetblockHistory serializes the operator's firewall/database mutation and history removal with auto-block cycles. clear may be nil for history-only cleanup; it must not call another auto-block state operation.
func FormatCleanResult ¶
func FormatCleanResult(r CleanResult) string
FormatCleanResult returns a human-readable summary of a clean operation.
func FormatDBCleanResult ¶
func FormatDBCleanResult(r DBCleanResult) string
FormatDBCleanResult formats a DBCleanResult for terminal output.
func FullScanMaxFileBytes ¶
FullScanMaxFileBytes converts cfg.Thresholds.FullScanMaxFileMB to a byte limit for per-file content reads during a full-scan job. A configured value of 0, negative, or above the validated maximum falls back to 16 MiB so the caller never gets an unconstrained or overflowed limit from a bad field.
func GetScanHomeDirs ¶
GetScanHomeDirs returns the list of home directories to scan. When ctx carries an account scope (via ContextWithAccountScope), only that account is returned. Otherwise every entry under every account root is read. Nil ctx is tolerated for legacy callers and treated as host-wide. Callers that need the directory path use scanHomeDirPath on each entry.
func HostingAccountForUser ¶
HostingAccountForUser returns name when it is a hosting account: its passwd home directory sits directly under a configured account root. Root, system and service users, unknown names and the lookup sentinel resolve to "". The passwd cache is the same one process findings use for uid resolution, so tests point both at one fixture file.
func IPResponseAnswersFinding ¶
IPResponseAnswersFinding is the alert.IPResponsePolicy for suppress_blocked_alerts. A block or challenge on the source address answers only attacker-side findings: attacker activity or attempted access that is not evidence of compromise. Once the source is stopped the operator has nothing left to do. Compromise evidence, successful logins and audit events stay visible even when their source address is already blocked.
A challenge answers only the findings challenge routing would send to the gate. The address being on the challenge list is itself proof the gate is in use, so the policy does not consult challenge.enabled.
func InitAutoBlockQueueHealth ¶
InitAutoBlockQueueHealth observes existing retries before daemon consumers start, including when automatic blocking is disabled. It never changes state.
func InlineQuarantine ¶
InlineQuarantine moves a file to quarantine immediately if it passes the high-confidence validation gates. Called from fanotify's analyzeFile to quarantine malware without waiting for the 5-second batch dispatcher. The data parameter is the file content already read by the caller (avoids TOCTOU re-read). Pass nil to read from path. Returns the quarantine path and true if the file was quarantined.
func InlineQuarantineGated ¶
func InlineQuarantineGated(cfg *config.Config, f alert.Finding, path string, data []byte) (string, bool)
InlineQuarantineGated applies the operator's quarantine policy before InlineQuarantine moves anything. The realtime fanotify path detects malware continuously, but moving a file is a customer-impacting auto-response action: it must honor the same master switch and quarantine opt-in as the batch AutoQuarantineFiles dispatcher, never act on detection alone. An operator in monitor mode (auto-response off, or quarantine_files off) gets the alert without having files moved out from under them.
func InlineQuarantineGatedIdentified ¶
func InlineQuarantineGatedIdentified(cfg *config.Config, f *alert.Finding, path string, data []byte, scanned os.FileInfo) (string, bool, *alert.Finding)
InlineQuarantineGatedIdentified applies the auto-response policy gate and then quarantines the exact file the caller scanned. See InlineQuarantineIdentified for why the identity matters. Marks the finding evaluated only once it reaches the shared budget gate.
func InlineQuarantineIdentified ¶
func InlineQuarantineIdentified(f alert.Finding, path string, data []byte, scanned os.FileInfo) (string, bool)
InlineQuarantineIdentified is InlineQuarantine with the identity of the file the caller actually scanned. The realtime scanner reads content from the fanotify event descriptor, so passing that descriptor's stat pins the move to the object that was examined: a file replaced between detection and quarantine fails the identity check instead of being moved in place of the malware. A nil identity keeps the older path-based behaviour for callers that began from a path in the first place, such as the batch dispatcher.
func IsBenignPHPStub ¶
IsBenignPHPStub reports whether the reachable code region of a PHP file consists only of whitespace and comments, or terminates with a literal-argument die / exit, or __halt_compiler before any other statement. Files matching either shape cannot execute attacker-controlled code via a web request: PHP either runs to EOF emitting nothing, or hits the terminator and stops with the remaining bytes unreachable.
The recogniser is content-shape only -- it does not look at the path, filename, parent directory, or whether a plugin is installed. An attacker cannot bypass it by naming a payload to mimic a known-plugin file because the gate fails the moment any executable statement appears before a terminator. Conversely a legitimate plugin that writes a stub-shaped working file (BackWPup writes "<?php //<json>" for job state and "<?php\n//path1\n//path2..." for folder caches) is recognised regardless of where it puts the file.
Other detectors -- signature scans, YARA, suspicious filename, the webshell name list -- still run on the file in their own pipelines. Only the path-only "anomalous PHP location" warning is suppressed for files that this recogniser accepts.
func IsBenignPHPStubBytes ¶
IsBenignPHPStubBytes is the buffer-only variant. The realtime fanotify path uses it on the bytes it already read from the file descriptor; IsBenignPHPStub provides the path-based entry point for the polled fileindex scan. Both rely on the same parser so realtime and scheduled scans agree on which files are stubs.
The parser tokenises the leading region of the buffer:
- Optional UTF-8 BOM and whitespace, then the literal "<?php" opener. The short-echo opener "<?=" is rejected because it emits output. A "<?phpfoo" run-together opener is rejected because PHP requires whitespace (or EOF) after the tag.
- Repeatedly accept whitespace, line comments ("//..." or "#..." up to newline or "?>"), and balanced block comments ("/* ... */"). A "/*" without a matching "*/" inside the scanned window is rejected -- we cannot prove the rest of the file is comment.
- Accept die, exit, and __halt_compiler as terminators. die and exit may carry a single literal argument (a non-interpolating string or a decimal integer); __halt_compiler takes none. Once seen, the rest of the buffer is treated as unreachable.
- Reject any closing "?>" tag (would allow HTML escape and a later "<?php" re-entry that this gate does not analyse).
- Reject any other identifier (return, if, system, eval, function, class, ...) and any stray punctuation ("$", "(", "=", ";", ...). Those are statements we cannot prove benign.
- If the loop reaches EOF in a complete buffer having only seen whitespace and comments, accept: PHP outputs nothing and executes nothing.
func IsBenignPHPStubBytesComplete ¶
IsBenignPHPStubBytesComplete is like IsBenignPHPStubBytes, but complete tells the parser whether buf contains the entire file. Comment-only stubs require a complete buffer; terminators do not, because bytes after them are unreachable to PHP.
func IsCloudflareIP ¶
IsCloudflareIP reports whether ip is inside the published Cloudflare ranges the daemon last refreshed.
func IsContentReverifiable ¶
IsContentReverifiable reports whether a check type is re-evaluated by re-running the content classifier (vs presence-only).
func IsDBObjectKind ¶
IsDBObjectKind reports whether s is one of the four valid kinds. Used by the CLI subcommand to validate user input before opening a connection.
func IsDerivedCorrelationCheck ¶
IsDerivedCorrelationCheck reports whether name is an output of correlation.
func IsExternalDest ¶
IsExternalDest returns true if the destination is an email address with a domain not in the local domains set. A pipe is a command, even when its arguments contain an address.
func IsInfraIP ¶
IsInfraIP reports whether ip is infrastructure the daemon must never act against: an operator infra entry (CIDR or address) or a Cloudflare edge. One implementation serves scans and the realtime path alike.
func IsKnownForwarder ¶
IsKnownForwarder checks if a forwarder rule matches the known forwarders suppression list.
func IsPipeForwarder ¶
IsPipeForwarder returns true if the destination is a pipe forwarder, excluding pipes whose executed command is a cPanel built-in.
func IsVerifiedCMSFile ¶
IsVerifiedCMSFile checks if a file at the given path matches a known-clean CMS core file by comparing its SHA256 hash against the cache.
The cache is keyed by SHA256 hash alone (not path+hash) - this is correct:
- SHA256 preimage resistance makes it computationally infeasible for an attacker to craft a file that produces the same hash as a legitimate WP core file. Birthday attacks do not apply here because the attacker must hit a specific pre-existing hash, not merely find any collision.
- If file content matches a known WP core file byte-for-byte, it IS that file regardless of where it is located on disk. The path is irrelevant to whether the content is clean.
func IsVerifiedCMSHash ¶
IsVerifiedCMSHash reports whether a content hash belongs to a verified CMS core file. Callers that already hold the content -- the realtime scanner hashes the event descriptor -- use this instead of IsVerifiedCMSFile, whose re-read resolves the path again and can hash something other than what was examined.
func IsWPTranslationCacheBytesComplete ¶
IsWPTranslationCacheBytesComplete reports whether buf is exactly a WordPress PHP translation cache: the "<?php" opener, the keyword "return", a single PHP array literal whose elements are only string/integer scalars (optionally concatenated string literals, as GlotPress joins plural forms with a "\0" separator) or nested arrays of the same, then a ";" and nothing else. WordPress 6.5+ auto-generates these as pure data return maps (*.l10n.php); each one previously opened a sensitive-dir Warning incident.
This is a content-structure recognizer, not a path or filename allowlist. A variable, a function call, string interpolation, a concatenation operand that is not a literal, a closing "?>" tag, or any statement after the array makes it return false, so an attacker cannot smuggle code into a file shaped like a translation cache. complete must be true: a truncated buffer cannot prove the unseen tail carries no code, so it is never suppressed.
func IsWPVersionDataBytesComplete ¶
IsWPVersionDataBytesComplete recognizes only literal assignments to the variables WordPress reads from its version file. Matching an installed copy alone cannot establish that a short-lived version probe carried no payload.
func LatestPurgeCheckNamesForReducedDeep ¶
func LatestPurgeCheckNamesForReducedDeep() []string
LatestPurgeCheckNamesForReducedDeep returns the emitted finding names owned by the reduced deep set used while fanotify covers filesystem events.
func LatestPurgeCheckNamesForTier ¶
LatestPurgeCheckNamesForTier returns every emitted finding name owned by a tier. The daemon uses this to replace a tier's current scan output without retaining stale findings from prior runs.
func LooksLikeCpanelRestoreStaging ¶
LooksLikeCpanelRestoreStaging recognises files inside cPanel's pkgacct/restorepkg staging tree. cPanel extracts the user backup as root into /home/cpanelpkgrestore.TMP.work.<id>/ for inspection, then re-extracts it under the user identity into /home/<account>/. Both extractions raise events; the user-context one carries the real signal, so the staging-side alert is a duplicate.
The recogniser requires the marker to sit directly under /home (the only place cPanel ever creates it) plus a non-empty alphanumeric id of >=2 chars. A user account at /home/<user>/ cannot create siblings of itself, so this gate cannot be spoofed by a non-root attacker.
func LooksLikeWPOptimizeProbeByPath ¶
LooksLikeWPOptimizeProbeByPath recognises WP-Optimize's per-server probe files using path structure alone. WP-Optimize writes tiny <?php files to /wp-content/uploads/wpo/.../test.php to test whether the host honours certain Apache/Nginx directives.
Path-only gates (no content read):
- Path lies under /wp-content/uploads/wpo/.
- The basename is exactly "test.php" (the literal filename WP-Optimize uses for these probes; an attacker dropping /uploads/wpo/webshell.php fails this gate and continues to the standard alert).
- The wp-optimize plugin directory is actually present in this site's wp-content/plugins/ tree (filesystem stat).
The realtime path additionally applies a content shape gate before suppressing the duplicate alert, so the path predicate here stays narrow to the literal probe filename and installed plugin directory.
func LookupUser ¶
LookupUser returns the username for uid, or "uid:<n>" if not resolvable. Safe for concurrent use; the underlying cache is shared across the daemon.
func MailOwner ¶
MailOwner returns the hosting account for a mailbox or bare domain, or "" when the mapping is unavailable. It never returns the mailbox or domain itself as an owner.
func MatchCrontabPatternsDeep ¶
MatchCrontabPatternsDeep is matchCrontabPatterns plus a single base64 decode pass: it pulls out base64 candidates from content and re-runs pattern matching on the decoded bytes. Catches attackers who wrap the `base64 -d|bash` pipe chain in an outer base64 layer so the literal markers never appear in the cron file as written. Single decode depth; no recursion. cfg nil uses the built-in defaults; pass the live operator config to honour `thresholds.crontab_base64_blob_max_bytes`.
func MergeModSecUserConfSection ¶
MergeModSecUserConfSection merges CSM's ModSecurity rules payload into the current contents of a modsec2.user.conf, confining CSM to its marker-delimited section so operator-maintained rules outside the section survive every deploy. It is exported because three call sites write this file (the WAF check cycle here, `csm install`, and the daemon startup config deploy); routing them all through one merge guarantees no caller ever whole-file-overwrites operator rules.
existing is the current file contents (nil for a missing file). merged is only meaningful when changed is true; changed=false means the file already carries the wanted section and must not be rewritten.
func MigrateWPCronCrontabs ¶
MigrateWPCronCrontabs upgrades CSM-managed wp-cron crontab lines installed by older releases (synchronized */N schedule, no overlap lock) to the current staggered format. The perf_wp_cron finding never re-fires once DISABLE_WP_CRON is set, so already-fixed accounts can only be reached by walking the spool directly. Gated on the same fix_wp_cron opt-in as the install path; returns the number of crontabs rewritten.
func NextSensitiveDigests ¶
func NextSensitiveDigests(prev map[string]SensitiveFileState, paths []string) (map[string]SensitiveFileState, map[string][]byte)
NextSensitiveDigests builds the state snapshot for a refresh cycle and returns the exact readable regular-file content behind each digest. A path whose content cannot be read keeps its previous digest so a transient read error does not surface as a content change. Non-regular objects retain type identity without being read. Paths absent from paths drop out. prev is only read and is never mutated.
func ObserveOperatorBlock ¶
ObserveOperatorBlock reports an operator-initiated force block (CLI or web UI) into the outcome metric. Force blocks bypass the dry-run gate, so a nil error means the block landed live.
func PHPConfigRealtimeRootPatterns ¶
PHPConfigRealtimeRootPatterns returns the startup-time path patterns used to admit php.ini writes into the fanotify analyzer. Explicit account_roots are authoritative. On cPanel, account homes are intentionally broader than the primary public_html root because addon domains can live anywhere below an account and on alternate home mounts.
func PHPConfigSecurityBypasses ¶
PHPConfigSecurityBypasses returns the strong, low-false-positive signals that a PHP ini file weakens security: disable_functions cleared or neutralized, allow_url_include enabled, or open_basedir removed. Used for newly-seen files, where the noisier per-function diffing of analyzePHPINI would flag benign partial disable lists.
func PHPTerminatesImmediately ¶
PHPTerminatesImmediately recognizes a leading exit, die, or __halt_compiler in unencoded PHP source, with at most one plain literal argument. Callers must establish that PHP source conversion is disabled before using this to suppress findings: conversion can remove even a raw terminator keyword. Only the opening tag and whitespace may precede it. Unlike the broader stub parser, it never accepts comments before the terminator. A completed terminator can be recognized from a partial head; EOF alone is not proof.
func PHPTerminatesImmediatelyAt ¶
PHPTerminatesImmediatelyAt is PHPTerminatesImmediately plus the offset just past the terminator statement, so a caller can judge the unreachable tail.
func PathMatchesIgnore ¶
matchGlob reports whether path is covered by an operator suppression pattern.
Matching is tried in order:
- filepath.Match against the basename ("*.php", "*.log") and the full path.
- For a leading-any-depth glob pattern, a substring match of the wildcard-stripped residue -- but ONLY when that residue still contains a path separator with literal content (e.g. "*/node_modules/*" -> "/node_modules/"). This preserves the "directory anywhere in the path, at any depth" intent without broadening anchored full-path globs like "/tmp/safe/*" into recursive subtree suppressions.
- For a pattern with no wildcards, a literal substring match, so an operator can suppress a directory ("/uploads/") or a filename ("adminer.php").
The separator requirement in step 2 is the fix for an over-suppression footgun: the previous code stripped every "*" and substring-matched the remainder, so "*.php" became the bare token ".php" and silenced every file whose path merely contained ".php" -- turning a narrow pattern into a whole-subtree allowlist an attacker could hide a webshell in. PathMatchesIgnore reports whether path is covered by any of the operator's suppressions.ignore_paths patterns, using the same glob semantics the content checks apply. Exported so the real-time watchers honour the same suppression list instead of maintaining a second interpretation of it.
func PerfCheckNamesForTier ¶
PerfCheckNamesForTier returns the perf_* check names registered in the given tier. Used by the daemon to perform an atomic purge-and-merge when storing findings.
func PkgManagerRecentlyActive ¶
PkgManagerRecentlyActive reports whether any package-manager log was modified within the provenance demotion window. Exported for the fanotify /tmp-executable demotion, which gates on the same evidence as rescoreSensitive.
func PluginInventoryQueueStatus ¶
func PluginInventoryQueueStatus(now time.Time) queuehealth.Status
PluginInventoryQueueStatus includes sites waiting for a worker and inventories still executing or committing their result. Concurrent refreshes share no cap.
func PruneExemptAutoSubnets ¶
PruneExemptAutoSubnets removes auto-response subnet blocks whose CIDR now intersects the DoS-exempt set (operator ranges or mail-provider overlay). Only entries with Source == firewall.SourceAutoResponse are touched; manual, CLI, web-UI, challenge, whitelist, dyndns, system, and unknown-source blocks are left untouched. If b does not implement subnetManager, returns 0. UnblockSubnet errors are logged and the entry is not counted as pruned.
func QuoteIdent ¶
QuoteIdent returns a backtick-quoted MySQL identifier, or an error if the input is empty, longer than 64 bytes, or contains characters outside the safe class. Used at every site where an attacker- controlled object name (trigger / event / routine / schema) would otherwise reach a SQL string concatenation.
The safe class is intentionally narrow: standard MySQL allows more (digits-only names, dotted names, $-prefixed) but the cleaner only needs to handle CMS-shaped identifiers and operator-typed schema names. Rejecting anything weirder is cheaper than reasoning about edge cases in dynamic SQL.
func RealtimeDocumentRootPatterns ¶
RealtimeDocumentRootPatterns returns the served roots used by realtime content detectors. cPanel's domain map is authoritative for addon domains and accounts on alternate home mounts; the conventional public_html glob remains the fallback when that map is unavailable.
func RecordSelfWrite ¶
RecordSelfWrite registers that CSM remediation just wrote content to a sensitive watched file. A current file is also recorded durably with its identity; a provisional record made before the write stays memory-only.
func RecordUnattributedActiveSet ¶
RecordUnattributedActiveSet records the unattributed rows of the latest-state active set after a merge. Same locking contract as ReportUnattributedCorrelation.
func RegisterDirectSMTPEgressMetrics ¶
RegisterDirectSMTPEgressMetrics binds the per-finding counter to reg. Production callers should pass metrics.Default(); tests pass metrics.NewRegistry() to keep registration isolated.
func RemoveAFAlgSeccompDropIns ¶
RemoveAFAlgSeccompDropIns deletes every CSM-managed seccomp drop-in found on disk and runs systemctl daemon-reload + reload-or-restart per touched unit so the seccomp filter is dropped from running processes. Idempotent: a unit without our drop-in is skipped.
Returns the list of units whose drop-in was removed.
func RemoveModSecUserConfSections ¶
RemoveModSecUserConfSections removes only content owned by CSM from the shared ModSecurity user configuration. Operator bytes outside the exact CSM marker lines are preserved.
func ReportUnattributedCorrelation ¶
ReportUnattributedCorrelation records unattributed rows from a per-batch derivation through the process-wide reporter. Callers invoke it after releasing any state store lock; it never re-enters the store.
func ReputationQueueStatus ¶
func ReputationQueueStatus(now time.Time) queuehealth.Status
ReputationQueueStatus reads memory only, including queries outliving a check.
func ResetAttributionHealthForTest ¶
func ResetAttributionHealthForTest()
ResetAttributionHealthForTest replaces the process-wide reporter with a fresh one. Test-only.
func ResolveWPCronRoots ¶
ResolveWPCronRoots returns the same validated roots used by CheckWPCron. Remediation callers use it so a finding from an addon domain remains inside the exact root authorized by cPanel's map.
func ResolveWebRoots ¶
ResolveWebRoots returns the list of directory paths CSM should scan for web-facing content (wp-config.php, .htaccess, public_html trees, etc.).
Resolution order:
- If cfg.AccountRoots is set, expand each glob and return the result. Explicit config always wins.
- On cPanel hosts (detected via platform.Detect), fall back to /home/*/public_html for backward compatibility.
- On non-cPanel hosts with no config, return an empty list. Callers should treat this as "no scanning" and skip cleanly.
Each returned path is an absolute directory that exists on disk.
func RestoreVirtualPatchBackup ¶
func RestoreVirtualPatchBackup(backupPath string, target *safepath.Target, meta QuarantineMeta) error
RestoreVirtualPatchBackup reverts only the captured patch, using the pinned destination directory so tenant renames cannot redirect reads or changes.
func ReverifyStaleFindingsStats ¶
func ReverifyStaleFindingsStats(ctx context.Context, store LatestFindingStore) ([]ContentReverifyDismissal, ReverifySweepStats, bool)
ReverifyStaleFindingsStats is ReverifyStaleFindingsContext with a summary of everything the sweep looked at, including the findings it could not check.
func RunAccountScan ¶
RunAccountScan runs all applicable checks scoped to a single cPanel account. Returns findings for that account only. Does NOT trigger auto-response actions.
This is a thin wrapper around RunAccountScanWithOptions using DefaultAccountScanOptions so all existing callers retain their current behaviour.
func RunAccountScanWithOptions ¶
func RunAccountScanWithOptions(ctx context.Context, cfg *config.Config, store *state.Store, account string, opts AccountScanOptions) []alert.Finding
RunAccountScanWithOptions is the options-aware entry point for per-account scans. Scope is propagated through ctx via ContextWithAccountScope, so parallel scans of different accounts no longer block on a single process-wide mutex and never bleed scope into each other.
func RunAll ¶
RunAll runs critical checks always. Deep checks run if throttle allows or ForceAll is set. The second return value is the per-scan purge name list scoped to the checks that actually executed this cycle.
func RunAllDryRun ¶
RunAllDryRun is the dry-run variant of RunAll for `csm baseline`.
func RunHardeningAudit ¶
func RunHardeningAudit(cfg *config.Config) *store.AuditReport
RunHardeningAudit runs all hardening checks and returns a report. Pure function — reads system state only, no store access.
func RunReducedDeep ¶
RunReducedDeep runs only the deep checks that fanotify can't replace. Used by the daemon when fanotify is active.
Filesystem and content scans remain scheduled because fanotify misses renames, permission changes and files planted before daemon startup.
The second return value is the per-scan purge name list scoped to the checks that actually executed this cycle.
func RunReducedDeepWithContext ¶
func RunReducedDeepWithContext(ctx context.Context, cfg *config.Config, store *state.Store) ([]alert.Finding, []string)
RunReducedDeepWithContext is RunReducedDeep with a caller-owned parent context for daemon shutdown cancellation.
func RunTier ¶
RunTier runs only the specified tier of checks. The second return value is the per-scan purge name list (emitted finding aliases owned by the checks that actually executed this cycle); pass it to StoreLatestScanFindings so throttled-out checks keep their prior findings.
Passes the requested dry-run state into runParallel via a scoped parameter rather than the previous package-level toggle, so concurrent periodic scanners no longer race with a manual `csm check` invocation.
func RunTierDryRun ¶
RunTierDryRun is the dry-run variant of RunTier: auto-response actions are skipped. Used by `csm check*` socket commands and the legacy CLI.
func RunTierDryRunWithContext ¶
func RunTierDryRunWithContext(ctx context.Context, cfg *config.Config, store *state.Store, tier Tier) ([]alert.Finding, []string)
RunTierDryRunWithContext is RunTierDryRun with a caller-owned parent context. Control-socket scans use it to collect coverage gaps without enabling auto-response actions.
func RunTierWithContext ¶
func RunTierWithContext(ctx context.Context, cfg *config.Config, store *state.Store, tier Tier) ([]alert.Finding, []string)
RunTierWithContext is RunTier with a caller-owned parent context. Daemon periodic scans pass their shutdown context here so an interrupted scan does not stall process exit.
func SSHAcceptedLoginFinding ¶
SSHAcceptedLoginFinding parses an sshd "Accepted <method> for <user> from <ip> port <n>" line and reports it unless the address is infrastructure. The daemon's realtime log watcher calls it so a login seen live and the same line re-read by CheckSSHLogins carry one identity; without that the state store sees two findings and the operator gets one login reported twice.
func SetASNLookup ¶
SetASNLookup wires the GeoLite2-ASN resolver used by the outbound connection scan. Passing nil clears it.
func SetAccountOwnerLookupForTest ¶
SetAccountOwnerLookupForTest replaces the domain-to-owner mapping and returns a function that restores it. Test-only seam for packages that cannot reach this package's filesystem fakes.
func SetBotVerifier ¶
func SetBotVerifier(v *threatintel.AsyncBotVerifier, get func(net.IP, string) (bool, bool))
SetBotVerifier installs the daemon-lifetime async verifier and cache reader. Called from daemon.go after the store and goroutine are ready.
func SetChallengeIPList ¶
func SetChallengeIPList(list ChallengeIPList)
SetChallengeIPList sets the challenge IP list for routing.
func SetCloudflareNets ¶
func SetCloudflareNets(cidrs []string)
SetCloudflareNets updates the cached Cloudflare IP ranges. Called by the daemon after fetching CF IPs.
func SetCmdRunner ¶
func SetCmdRunner(r CmdRunner)
SetCmdRunner replaces the command runner. Used by tests to inject mocks.
func SetGlobalThreatDBForTest ¶
func SetGlobalThreatDBForTest(statePath string) func()
SetGlobalThreatDBForTest installs a freshly-constructed threat DB rooted at statePath, bypassing the once-guard, and returns a function that restores the previous global. For tests only: lets a test exercise the threat-DB path without permanently polluting the global for order-dependent tests.
func SetHostingAccountLookupForTest ¶
SetHostingAccountLookupForTest replaces the user-to-account mapping and returns a function that restores it. Test-only seam for packages that cannot reach this package's passwd and account-root fakes.
func SetIPBlocker ¶
func SetIPBlocker(b IPBlocker)
SetIPBlocker installs the firewall engine for auto-blocking. Safe to call concurrently with AutoBlockIPs: each call publishes the new blocker atomically and any in-flight scan keeps the snapshot it already loaded.
func SetOS ¶
func SetOS(o OS)
SetOS replaces the filesystem provider. Used by tests to inject mocks.
func SetPHPTaintAnalyzer ¶
func SetPHPTaintAnalyzer(a PHPTaintAnalyzer)
SetPHPTaintAnalyzer installs the supervised worker. Passing nil removes it, after which every candidate is reported as an unexamined coverage gap rather than analyzed in this process.
func SetSelfWriteStore ¶
SetSelfWriteStore gives the self-write ledger somewhere durable to record what CSM wrote. Without it, a daemon restart -- or a crontab the cPanel wrapper reformats after CSM hands it over -- makes CSM's own write look like a third-party change to the sensitive-file detectors.
func SetWebProbe ¶
func SetWebProbe(p webProbe)
SetWebProbe replaces the reachability prober. Test-only seam.
func ShouldCleanInsteadOfQuarantine ¶
ShouldCleanInsteadOfQuarantine returns true if the file should be cleaned (surgical removal) instead of quarantined (full removal). WP core files and plugin files are better cleaned - removing them breaks the site. Unknown standalone files (droppers, webshells) should be quarantined.
func ShouldDemoteSeverity ¶
func ShouldDemoteSeverity(f alert.Finding, res VerifyResult) bool
ShouldDemoteSeverity reports whether a verdict retires a remediated but unproven finding from the live queue. It is never a clear: an attacker must not retire a finding by editing the file. Demoting an already-Warning finding would be churn.
The unattended sweep and the operator's Re-check both ask this, so the two cannot disagree about what a verdict means.
func ShouldRestoreSeverity ¶
func ShouldRestoreSeverity(f alert.Finding, res VerifyResult) bool
ShouldRestoreSeverity reports whether an automatic demotion must be reversed. A demotion holds only while the replacement keeps satisfying the inert-content gate. Restore on a positive match and on every uncertain or newly-active shape alike; otherwise a second edit into a detection gap would leave live malware at Warning.
func StampContentFingerprint ¶
StampContentFingerprint records the detection-time content fingerprint on a content-reverifiable finding so the Re-check / sweep can later distinguish a superseded-heuristic false positive from a file edited after detection. A producer that analyzed an already-open snapshot may supply its exact hash; retain that fingerprint instead of reopening a path that may now name different content. No-op for non-content findings or findings without a path.
func StoreLatestScanFindings ¶
StoreLatestScanFindings replaces the latest findings owned by a scan, then rebuilds derived correlation findings from the merged current set. One-shot auto-response actions stay in history and alerts, not the active findings view.
func StoreLatestScanFindingsWithCoverage ¶
func StoreLatestScanFindingsWithCoverage(st *state.Store, purgeChecks []string, findings []alert.Finding, coverage *state.ScanCoverage)
StoreLatestScanFindingsWithCoverage retires only completed checks or scopes and preserves current findings for file gaps in the same atomic operation.
func StoreLatestScanFindingsWithGaps ¶
func StoreLatestScanFindingsWithGaps(st *state.Store, purgeChecks []string, findings []alert.Finding, gapPaths map[string]map[string]bool)
StoreLatestScanFindingsWithGaps preserves the latest state for files a completed scan could not examine while replacing its covered state. gapPaths contains the lexical and resolved aliases captured when each gap occurred; the state store applies that frozen set under the same lock as the purge.
func SwapUIDCacheForTest ¶
func SwapUIDCacheForTest(path string) func()
SwapUIDCacheForTest is the exported form of swapDefaultUIDCacheForTest for packages whose producers resolve uids through LookupUser.
func VirtualPatchExposedFindings ¶
func VirtualPatchExposedFindings(_ *config.Config, findings []alert.Finding, apply bool) []alert.Finding
VirtualPatchExposedFindings applies (apply=true) or previews (apply=false) a deny rule for each virtual-patchable web_exposed_* finding, deduplicated by path. It returns one auto_response action finding per file.
func WPCoreQueueStatus ¶
func WPCoreQueueStatus(now time.Time) queuehealth.Status
WPCoreQueueStatus includes selected installations through checksum execution, result collection and verified-file caching. Concurrent scans share no cap.
func WebRootPatterns ¶
WebRootPatterns returns the configured web-root globs, including the platform default when the operator did not set account_roots.
func WithModSecReload ¶
func WithModSecReload(ctx context.Context, r *ModSecReloadReconciler) context.Context
WithModSecReload lets daemon scans share their startup reconciler. Contexts without one can still deploy rules, but leave activation to the daemon.
func WriteAFAlgMarker ¶
func WriteAFAlgMarker() error
WriteAFAlgMarker forces the canonical marker content to disk regardless of current state. Used by `csm harden --copy-fail` to ensure subsequent EnforceAFAlgBlocked() runs see a valid marker even on first install.
Types ¶
type AFAlgEvent ¶
type AFAlgEvent = afAlgEvent
AFAlgEvent is the package-public view of afAlgEvent used by callers outside internal/checks (the daemon's live audit-log listener emits findings derived from this shape).
func ParseAFAlgEventLine ¶
func ParseAFAlgEventLine(line string) (AFAlgEvent, bool)
ParseAFAlgEventLine is the exported alias of parseAFAlgEvent for the daemon's live listener. The unexported form stays internal so the rest of this package can refer to the type by its short name.
type AFAlgKernelState ¶
type AFAlgKernelState struct {
BuiltIn bool // CONFIG_CRYPTO_USER_API_AEAD=y in the running kernel
Modular bool // CONFIG_CRYPTO_USER_API_AEAD=m (loadable module exists)
ConfigReadable bool // /boot/config-$(uname -r) or /proc/config.gz was parseable
LivepatchActive bool // KernelCare/kpatch has applied a CVE-2026-31431 patch
}
AFAlgKernelState is the assembled view of how the running kernel exposes AF_ALG. Used by the hardening audit, by csm harden, and by the live-monitor coordinator to decide whether protection is needed.
func ObserveAFAlgKernelState ¶
func ObserveAFAlgKernelState() AFAlgKernelState
ObserveAFAlgKernelState is the exported alias for cmd/csm and the daemon. Production code inside this package uses the unexported form directly.
func (AFAlgKernelState) IsCopyFailExploitable ¶
func (s AFAlgKernelState) IsCopyFailExploitable() bool
IsCopyFailExploitable reports whether this kernel is currently vulnerable to Copy Fail (CVE-2026-31431). Used by the daemon's live-monitor coordinator to skip starting the listener entirely on hosts that don't need protection — saving the inotify watch + tick loop for hosts that actually face the threat.
Conservative defaults: when the kernel config is unreadable, treat the host as exploitable (better to over-monitor than miss). When a KernelCare livepatch is in place, treat as patched regardless of the underlying config — the syscall path itself is fixed.
func (AFAlgKernelState) String ¶
func (s AFAlgKernelState) String() string
String renders the kernel state for inclusion in operator-visible messages. The format is stable and short enough to embed in a single AuditResult.Message line.
type AccountScanOptions ¶
type AccountScanOptions struct {
MaxFiles int // 0 = uncapped path ranking
ForceContent bool // true = bypass clean-file content caches
ForceFileIndex bool // true = bypass file-index dir mtime cache, do not write live index
RespectIgnores bool // false = also scan suppressions.ignore_paths
MaxFileBytes int64 // 0 = use each check's existing per-file limit
}
AccountScanOptions controls how RunAccountScanWithOptions enumerates and content-scans an account. The zero value is NOT the default: MaxFiles 0 means uncapped. Callers use DefaultAccountScanOptions for normal behaviour.
func DefaultAccountScanOptions ¶
func DefaultAccountScanOptions(cfg *config.Config) AccountScanOptions
DefaultAccountScanOptions returns the options that reproduce today's RunAccountScan behaviour. All callers that want the existing cap and cache semantics should use this rather than constructing AccountScanOptions directly.
func FullScanOptions ¶
func FullScanOptions(cfg *config.Config, respectIgnores bool) AccountScanOptions
FullScanOptions builds the canonical option set for an uncapped full-scan audit job: no file cap, force content + file-index (bypass the clean-file and directory-mtime caches), and the configured per-file byte ceiling. respectIgnores is the only caller-chosen knob. Centralised here so the control handler and the WebUI enqueue path cannot drift on this security-relevant set.
func ScanOptionsFromContext ¶
func ScanOptionsFromContext(ctx context.Context) (AccountScanOptions, bool)
ScanOptionsFromContext retrieves the AccountScanOptions stored by ContextWithScanOptions. ok is false when the context carries no options, which callers should treat as "use defaults".
type ApplyBlockRequest ¶
type ApplyBlockRequest struct {
// ActionID preserves one admission identity across retries.
ActionID string
// FindingID is the original audit identity, captured before display truncation.
// Empty means this decision has no originating finding.
FindingID string
IP string
EngineReason string
Reason string
TTL time.Duration
Source string
}
ApplyBlockRequest describes one auto-response IP block. EngineReason is handed to the firewall engine (its provenance inference keys on it); Reason is the human evidence recorded in the threat DB, the tracker, and findings.
type ApplyBlockResult ¶
type ApplyBlockResult struct {
Outcome firewall.BlockOutcome
Findings []alert.Finding
}
ApplyBlockResult carries the engine outcome plus the auto_block findings the caller must route into its alert pipeline (scan batch or the daemon's async block recorder) so digests and alerting see every block source.
func ApplyBlock ¶
func ApplyBlock(cfg *config.Config, req ApplyBlockRequest) (ApplyBlockResult, error)
ApplyBlock is the single chokepoint for auto-response IP blocks issued outside the scan loop (challenge escalation, central intel, incident spray). It performs the block and the same evidence bookkeeping a scan auto-block gets: threat-DB row, blocked-IPs tracker entry, auto_block finding with the Cloudflare coverage warning, and permanent-block escalation counting. The scan loop shares the inner implementation and keeps its own batch semantics (rate limit, pending queue) around it.
Non-scan sources deliberately neither consume nor enforce auto_response.max_blocks_per_hour: challenge escalation and central intel are already gated upstream, and letting them starve or be starved by the scan budget would change containment behavior.
type AttributionReport ¶
type AttributionReport struct {
// Current holds, per check, the qualifying rows in the latest-state
// active set that carry no hosting owner and fall inside the correlation
// window at its most recent merge. Unstamped legacy rows also count.
// A later merge clears rows that gain an owner or age out of the window.
Current map[string]int
// Cumulative sums every unattributed row reported since start, by
// check, across active-set merges and per-batch derivations.
Cumulative map[string]int
// ActiveSetUpdates counts active-set merges since start.
ActiveSetUpdates int
// Since is when the first active set was recorded; zero before then.
Since time.Time
}
AttributionReport is the operator-facing view of correlation attribution. Current is what the latest-state active set looks like right now; Cumulative is the history since the daemon started. The two answer different questions: a producer that lost attribution for weeks and one that missed once look identical in a log line, and neither is visible in a health endpoint without this.
func AttributionHealth ¶
func AttributionHealth() AttributionReport
AttributionHealth reports the process-wide attribution state for the health snapshot and doctor.
type AutoBlockFlushResult ¶
AutoBlockFlushResult reports which phases of a coordinated firewall flush completed and whether its best-effort persisted-state snapshot was readable.
func FlushAutoBlockState ¶
func FlushAutoBlockState(statePath string, flush func() error) (AutoBlockFlushResult, error)
FlushAutoBlockState snapshots the engine's pre-flush IPs, runs an operator firewall flush, and clears the auto-block bookkeeping in one critical section. Without this the flush was self-reverting: surviving ThreatDB temp rows re-flagged every flushed IP through ip_reputation on the next scan and re-blocked it, and stale tracker entries suppressed re-block accounting. Tracker entries the engine never held are cleaned up too. Pending entries are kept - they are queued candidates, not blocks.
The firewall mutation and cleanup stay serialized with AutoBlockIPs so a concurrent scan either finishes before the snapshot or starts after cleanup with fresh evidence. Result.Flushed is true with a non-nil error when the firewall was flushed but bookkeeping cleanup was only partial. SnapshotErr is advisory because the tracker-side union still covers tracked auto-blocks.
type CMSHashCache ¶
type CMSHashCache struct {
// contains filtered or unexported fields
}
CMSHashCache stores SHA256 hashes of verified-clean CMS core files. After wp core verify-checksums confirms an installation is clean, all its core files are hashed and cached. The real-time scanner checks this cache before reporting signature matches - if a file's hash is in the cache, it's a known-clean CMS file and signature matches on it are false positives.
func GlobalCMSCache ¶
func GlobalCMSCache() *CMSHashCache
GlobalCMSCache returns the singleton cache, creating it on first call.
func (*CMSHashCache) Add ¶
func (c *CMSHashCache) Add(hash string, size int64)
Add inserts a file hash and its content size into the cache.
func (*CMSHashCache) Clear ¶
func (c *CMSHashCache) Clear()
Clear removes all cached hashes (used before rebuilding).
func (*CMSHashCache) Contains ¶
func (c *CMSHashCache) Contains(hash string) bool
Contains checks if a file hash is in the cache.
func (*CMSHashCache) MayContainSize ¶
func (c *CMSHashCache) MayContainSize(size int64) bool
MayContainSize reports whether a verified file of size bytes was cached. It lets realtime scanning reject attacker-sized files before hashing them.
func (*CMSHashCache) Size ¶
func (c *CMSHashCache) Size() int
Size returns the number of cached hashes.
type ChallengeIPList ¶
type ChallengeIPList interface {
Add(ip string, reason string, duration time.Duration)
AddNonEscalating(ip string, reason string, duration time.Duration)
Remove(ip string)
Contains(ip string) bool
}
ChallengeIPList abstracts the challenge IP list for routing.
func GetChallengeIPList ¶
func GetChallengeIPList() ChallengeIPList
GetChallengeIPList returns the current challenge IP list (for AutoBlockIPs skip check).
type ChallengeRouteRecord ¶
type ChallengeRouteRecord struct {
IP string `json:"ip"`
Check string `json:"check"`
At time.Time `json:"at"`
}
ChallengeRouteRecord is one IP routed to the challenge, for the web UI's recent-activity list.
type ChallengeUIStatsSnapshot ¶
type ChallengeUIStatsSnapshot struct {
RoutedByCheck map[string]int `json:"routed_by_check"`
Recent []ChallengeRouteRecord `json:"recent"`
}
ChallengeUIStatsSnapshot is the web-UI view of challenge routing: cumulative per-check counts since daemon start plus the most recent routes. It is a copy, safe for the caller to read without locking.
func ChallengeUIStats ¶
func ChallengeUIStats() ChallengeUIStatsSnapshot
ChallengeUIStats returns a copy of the current challenge routing stats for the web UI. Most recent route is last in Recent.
type CheckDispatchProgress ¶
type CheckDispatchProgress struct {
// contains filtered or unexported fields
}
CheckDispatchProgress is memory-only evidence for a scan's orchestration. All fields are guarded by the owning dispatch monitor's mutex.
func WithCheckDispatchProgress ¶
func WithCheckDispatchProgress(ctx context.Context) (context.Context, *CheckDispatchProgress)
WithCheckDispatchProgress binds subsequent check batches to this operation. A new binding isolates late callbacks from a previously canceled operation.
func (*CheckDispatchProgress) Snapshot ¶
func (p *CheckDispatchProgress) Snapshot(now time.Time) DispatchProgressSnapshot
Snapshot does not query contexts, the filesystem or the job store.
type CheckFunc ¶
CheckFunc is the signature for all check functions. The context is cancelled when the check times out so goroutines can exit.
type CheckInfo ¶
type CheckInfo struct {
Name string
Category string
Internal bool
// Correlation says how cross-account correlation treats this check.
// Every entry must set it; the zero value fails TestEveryCheckIsClassified.
Correlation CorrelationClass
// CorrelationReason names the policy that excludes an Ignored check. One
// of the reason constants in correlation_policy.go.
CorrelationReason string
// CorrelationGap documents a known missing producer identity path for an
// eligible check. It never changes eligibility.
CorrelationGap string
}
CheckInfo describes a single named check emitted as an alert.Finding.Check. Category groups related checks for display in the settings UI. Internal is true for checks that exist for plumbing (self-tests, plumbing findings) and should not appear in user-facing dropdowns like alerts.email.disabled_checks.
func LookupCheck ¶
LookupCheck returns the registry entry for name, if any.
func PublicCheckInfos ¶
func PublicCheckInfos() []CheckInfo
PublicCheckInfos returns all non-Internal checks grouped by category in the canonical category order (see checkCategoryOrder). Within a category names are sorted alphabetically. This is the list the settings UI shows for alerts.email.disabled_checks.
type CleanResult ¶
type CleanResult struct {
Path string
Cleaned bool
BackupPath string
Removals []string // descriptions of what was removed
Error string
// Refused marks a safety refusal without changing the customer file.
// It consumes attempt capacity but does not indicate a broken action.
Refused bool
}
CleanResult describes the outcome of a cleaning attempt.
func CleanInfectedFile ¶
func CleanInfectedFile(path string) CleanResult
CleanInfectedFile attempts to surgically remove malicious code from a PHP file while preserving the legitimate content. Always creates a backup first.
Cleaning strategies (tried in order): 1. @include injection - remove @include lines pointing to /tmp, eval, base64, or via variables 2. Prepend injection - remove malicious code blocks at start of file (entropy-validated) 3. Append injection - remove malicious code after closing ?> or end of PSR-12 file 4. Inline eval injection - remove eval(base64_decode(...)) single-line injections
type CmdRunner ¶
type CmdRunner interface {
Run(name string, args ...string) ([]byte, error)
RunAllowNonZero(name string, args ...string) ([]byte, error)
RunContext(parent context.Context, name string, args ...string) ([]byte, error)
RunContextStdout(parent context.Context, name string, args ...string) ([]byte, error)
RunWithEnv(name string, args []string, extraEnv ...string) ([]byte, error)
LookPath(file string) (string, error)
}
CmdRunner abstracts external command execution used by check functions. Production code uses realCmd{}; tests swap in a mockCmdRunner via SetCmdRunner().
RunContext returns stdout+stderr merged (CombinedOutput) and is fine for tools that only write to stdout. RunContextStdout returns stdout only and should be used when the command prints structured output (JSON, a URL, ...) on stdout and chatter (warnings, PHP notices, MySQL deprecations) on stderr -- mixing them there would corrupt the parse. RunContextStdout also surfaces context.DeadlineExceeded on timeout so callers can distinguish "no output" from "empty output".
type ContentReverifyDismissal ¶
type ContentReverifyDismissal struct {
Check string
Path string
Detail string
// Demoted and Promoted distinguish the outcomes for the audit log: a
// cleared finding is gone, a demoted one is still listed at a lower
// severity, a promoted one had an earlier demotion reversed.
Demoted bool
Promoted bool
}
ContentReverifyDismissal records one finding the sweep cleared, for the caller to audit-log (the checks package has no logger of its own).
func ReverifyStaleFindings ¶
func ReverifyStaleFindings(store LatestFindingStore) []ContentReverifyDismissal
ReverifyStaleFindings re-checks every auto-reverifiable finding in the store against current detection logic and dismisses those that are now confirmed stale: for content findings a file that is gone, or identical bytes the current classifier no longer flags; for web_exposed_* findings an exposure that a complete pinned probe no longer confirms. Dispatch goes through the verifier registry, so each family keeps its own safety invariant -- a still-present file is cleared only when its bytes are unchanged since detection, and an exposure only when a complete probe says the server no longer serves it. Returns the dismissed findings for the caller to log. Read-only except for dismissing confirmed-stale findings.
func ReverifyStaleFindingsContext ¶
func ReverifyStaleFindingsContext(ctx context.Context, store LatestFindingStore) ([]ContentReverifyDismissal, bool)
type CorrelationClass ¶
type CorrelationClass uint8
CorrelationClass is a check's role in cross-account correlation. Every registry entry sets one; the zero value fails the completeness test.
const ( // CorrelationUnclassified is the zero value and never valid. CorrelationUnclassified CorrelationClass = iota // CorrelationIgnored is never an input to correlation; a reason is required. CorrelationIgnored // CorrelationSecurityEvent: an attributed Critical counts toward coordinated_attack. CorrelationSecurityEvent // CorrelationMalwareArtifact is a SecurityEvent that also raises // cross_account_malware when the same check appears on two accounts. CorrelationMalwareArtifact // CorrelationDerived is an output of correlation and never an input. CorrelationDerived )
type CorrelationResult ¶
type CorrelationResult struct {
// Derived findings: the coordinated_attack result first, then one
// cross_account_malware result per qualifying check, sorted by check.
// Timestamps are left unset for the caller to stamp.
Derived []alert.Finding
// Unattributed counts qualifying input rows per check that carried no
// account identity. It is a snapshot for this call, not a running total,
// using the same window as the aggregates. Batch correlation has no age
// filter; persisted correlation also counts unstamped legacy rows.
Unattributed map[string]int
// CriticalAccounts is the distinct attributed account count after the
// same eligibility and time filters used to derive coordinated_attack.
CriticalAccounts int
}
CorrelationResult is the output of one CorrelateFindings call.
func CorrelateBatchFindings ¶
func CorrelateBatchFindings(findings []alert.Finding) CorrelationResult
CorrelateBatchFindings preserves dispatch grouping even when a scan carries forward a prior finding whose original timestamp lies outside the window.
func CorrelateFindings ¶
func CorrelateFindings(findings []alert.Finding) CorrelationResult
CorrelateFindings raises cross-account findings. Eligibility comes from the registry classification and identity from extractAccountFromFinding. Callers initialize platform.Detect before correlation so account roots come from its cache. It does not log or mutate its input.
type Correlator ¶
type Correlator struct {
// contains filtered or unexported fields
}
Correlator shares production rules with offline replay. Construct one with NewCorrelator to supply recording roots without triggering host discovery.
func NewCorrelator ¶
func NewCorrelator(window time.Duration, accountRoots []string) Correlator
NewCorrelator uses only the supplied account roots, with no host lookups. Window must be nonnegative; zero reproduces unbounded correlation.
func (Correlator) Correlate ¶
func (c Correlator) Correlate(findings []alert.Finding, at time.Time) CorrelationResult
Correlate derives aggregates at the supplied observation time. A zero time uses the newest non-derived input as its reference. Persisted state supplies the merge time so even an empty scan can expire old evidence.
type CoverageGaps ¶
type CoverageGaps struct {
// contains filtered or unexported fields
}
CoverageGaps holds file gaps and completed database scopes for a scan. The caller passes it to the atomic purge-and-merge operation so a concurrent update made after the scanner read LatestFindings cannot be retired by a stale carry-forward snapshot.
func WithCoverageGaps ¶
func WithCoverageGaps(ctx context.Context) (context.Context, *CoverageGaps)
WithCoverageGaps requests an atomic coverage-aware store operation from the caller. The runner publishes only its completed snapshot into the handle.
func (*CoverageGaps) Paths ¶
func (g *CoverageGaps) Paths() map[string]map[string]bool
Paths returns an isolated snapshot of the completed run's path gaps. Each inner key is a stable lexical or resolved alias captured during the scan.
func (*CoverageGaps) Snapshot ¶
func (g *CoverageGaps) Snapshot() *state.ScanCoverage
Snapshot includes both file gaps and completed database scopes from the same runner result. Callers must pass this snapshot to the atomic store operation.
type DBCleanResult ¶
type DBCleanResult struct {
Account string
Database string
Action string // "clean-option", "revoke-user", "delete-spam"
Success bool
Message string
Details []string // individual actions taken
BackupNames []string // names of backup options created
}
DBCleanResult describes the outcome of a database cleanup operation.
func DBCleanOption ¶
func DBCleanOption(account, optionName string, preview bool) DBCleanResult
DBCleanOption removes malicious script injections from a wp_option value. Creates a backup option before modifying. Returns a result describing what was done. If preview is true, reports what would be done without modifying the database.
func DBDeleteSpam ¶
func DBDeleteSpam(account string, preview bool) DBCleanResult
DBDeleteSpam deletes published posts matching spam patterns from a WordPress database. Only deletes posts of type 'post' with status 'publish' to avoid touching pages, attachments, or plugin data. If preview is true, reports counts without deleting.
Two restrictions keep a keyword match from destroying real content: the SQL LIKE result is re-tested Go-side on a word boundary, and only the patterns marked deletable are considered. Injections that live inside an otherwise legitimate page are out of scope here -- deleting the page would destroy the customer's own content, so those need the injected markup stripped instead.
func DBDropObject ¶
func DBDropObject(account, schema, kind, name string, preview bool) DBCleanResult
DBDropObject drops a single trigger / event / stored procedure / stored function from the operator-supplied account+schema, after:
- Validating the kind ("trigger" | "event" | "procedure" | "function").
- Validating that <schema> is one of the databases this account hosts. The account is taken from /home/<account>/* wp-config.php files; an attacker who can pass an arbitrary <schema> here gets no further than DROP'ping their own database.
- QuoteIdent on both <schema> and <name>, so identifier strings never participate in SQL string concatenation.
- SHOW CREATE the object and persist the result to the db_object_backups bbolt bucket as the backup -- replaying the CREATE SQL restores the object byte-for-byte.
- DROP the object.
preview=true short-circuits before step 4: the function reports what it would do (kind, schema, name, captured CREATE SQL) without touching the database.
Per spec: detection is always-on; drop is operator-driven.
func DBRevokeUser ¶
func DBRevokeUser(account string, userID int, demote, preview bool) DBCleanResult
DBRevokeUser revokes WordPress sessions for a specific user and optionally demotes them to subscriber role. If preview is true, reports what would be done without modifying the database.
func RestoreDBObjectBackup ¶
func RestoreDBObjectBackup(backupKey string) DBCleanResult
RestoreDBObjectBackup re-executes the captured CREATE SQL for a previously-dropped MySQL trigger / event / procedure / function. Looks up the row in the db_object_backups bbolt bucket by exact key; the caller (typically the web UI's cleanup-history page) supplies the key it got from the listing endpoint.
Per spec the operation is operator-driven: there is no auto- restore. The webui handler enforces the same.
type DirectSMTPEgressInput ¶
type DirectSMTPEgressInput struct {
UID uint32
User string
PID uint32
Comm string
Exe string
DstIP net.IP
DstPort uint16
MTA platform.MTAIdents
Process *processctx.ProcessContext
// Domain is an optional rDNS-resolved name for DstIP. When set, it
// is included in the finding details. Populating it is the caller's
// responsibility (off-path enrichment lands in Task 6).
Domain string
}
DirectSMTPEgressInput is the input to the evaluator. The caller (BPF connection consumer or legacy poller) builds it from the live event and passes the platform-resolved MTA allowlist as MTA.
Process is optional; when present the resulting finding includes the full process-ancestry tree. UID/User/PID/Comm/Exe are the live event fields used in finding details and account attribution.
type DispatchProgressSnapshot ¶
DispatchProgressSnapshot measures only the check batches owned by one caller. LastProgress remains available after the last wrapper exits, so its caller can time result handling without borrowing the completed check's budget.
type EnforceAction ¶
type EnforceAction int
EnforceAction is the discrete outcome of the pure enforcement decision. Each value corresponds to one operational step the impure wrapper takes.
const ( EnforceActionNoop EnforceAction = iota EnforceActionRestoreMarker EnforceActionUnloadModules EnforceActionRestoreAndUnload )
type EnforceResult ¶
type EnforceResult struct {
Action EnforceAction
MarkerPresent bool
MarkerValid bool
ModulesLoaded []string // names of currently-loaded targeted modules at start of call
MarkerWritten bool // wrapper wrote/restored the marker file this call
ModuleUnloaded bool // post-call /proc/modules shows targeted modules gone
Notes []string // operator-readable lines (warnings, stuck-module names)
}
EnforceResult describes what enforceAFAlgBlocked observed and did, in a shape both the CLI subcommand and the periodic Check function can format for the operator without re-deriving the same conclusions.
ModuleUnloaded reports the OBSERVED post-call state, not the syscall attempt: it is true only when /proc/modules no longer contains the targeted modules after `modprobe -r` ran. Use this field to distinguish "unload succeeded" from "unload attempted but module is in use".
func EnforceAFAlgBlocked ¶
func EnforceAFAlgBlocked() (EnforceResult, error)
EnforceAFAlgBlocked is the exported alias of enforceAFAlgBlocked for use by cmd/csm. The unexported form stays internal to the package so the periodic Check (Task 6) can call it without going through the export.
type IPBlocker ¶
type IPBlocker interface {
BlockIP(ip string, reason string, timeout time.Duration) error
UnblockIP(ip string) error
IsBlocked(ip string) bool
}
IPBlocker abstracts the firewall engine for auto-blocking. When set, blocks go through nftables firewall engine.
type LatestFindingStore ¶
type LatestFindingStore interface {
LatestFindings() []alert.Finding
// Each mutation takes the snapshot verification actually looked at, so a
// scan or realtime alert that refreshed the same key while the re-check was
// in flight is never overwritten by the older verdict.
DismissFindingIfLatest(expected alert.Finding) bool
DemoteLatestFinding(expected alert.Finding, severity alert.Severity) bool
RestoreLatestFindingSeverity(expected alert.Finding) bool
}
LatestFindingStore is the subset of the state store the sweep needs.
type ModSecReloadReconciler ¶
type ModSecReloadReconciler struct {
// contains filtered or unexported fields
}
ModSecReloadReconciler is owned by the daemon and shared by startup and all its scans. A CLI opening the store must not implicitly gain reload authority.
func (*ModSecReloadReconciler) Reconcile ¶
func (r *ModSecReloadReconciler) Reconcile(reloadCommand string) error
Reconcile activates sections already written by startup or the installer. Only failed reloads are repeated; metadata failures retry just the write.
type OS ¶
type OS interface {
ReadFile(name string) ([]byte, error)
ReadDir(name string) ([]os.DirEntry, error)
Stat(name string) (os.FileInfo, error)
Lstat(name string) (os.FileInfo, error)
Readlink(name string) (string, error)
Open(name string) (*os.File, error)
WriteFile(name string, data []byte, perm os.FileMode) error
MkdirAll(path string, perm os.FileMode) error
Remove(name string) error
Glob(pattern string) ([]string, error)
}
OS abstracts filesystem operations (read and write) used by check functions. Production code uses realOS{}; tests swap in a mockOS via SetOS().
type PHPExecutionOverlay ¶
type PHPExecutionOverlay struct {
// contains filtered or unexported fields
}
PHPExecutionOverlay is an immutable snapshot of inherited .htaccess PHP handler mappings for one directory. Realtime monitors can cache it and test arbitrary filenames without duplicating the periodic scanner's parser.
func ResolvePHPExecutionOverlay ¶
func ResolvePHPExecutionOverlay(docroot, fileDir string) PHPExecutionOverlay
ResolvePHPExecutionOverlay reconstructs the PHP handler mappings inherited by fileDir from docroot through the directory's own .htaccess file.
func (PHPExecutionOverlay) Executes ¶
func (o PHPExecutionOverlay) Executes(nameLower string) bool
Executes reports whether nameLower is handled as PHP by this overlay.
type PHPTaintAnalyzer ¶
PHPTaintAnalyzer is what the daemon supplies: an analyzer that runs in a process the caller can kill.
type QuarantineMeta ¶
type QuarantineMeta struct {
OriginalPath string `json:"original_path"`
Owner int `json:"owner_uid"`
Group int `json:"group_gid"`
Mode string `json:"mode"`
Size int64 `json:"size"`
QuarantineAt time.Time `json:"quarantined_at"`
OriginalModTime time.Time `json:"original_mtime,omitzero"`
Reason string `json:"reason"`
// FindingID ties the quarantine to the finding that caused it, using the
// same identifier the audit log and the action log emit. Empty when the
// quarantine came from an operator command rather than a detection.
FindingID string `json:"finding_id,omitempty"`
MessageID string `json:"message_id,omitempty"`
SpoolDir string `json:"spool_dir,omitempty"`
RestoreAction string `json:"restore_action,omitempty"`
ExpectedCurrentSHA256 string `json:"expected_current_sha256,omitempty"`
}
QuarantineMeta stores original file metadata alongside quarantined files.
func (*QuarantineMeta) UnmarshalJSON ¶
func (m *QuarantineMeta) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts the timestamp spelling used by historical manual fixes. Missing original mtimes remain unknown; archive mtimes are not evidence of when the original was modified.
type RDNSCache ¶
type RDNSCache struct {
// contains filtered or unexported fields
}
RDNSCache is a small TTL cache around reverse DNS lookups. Cached negative results (resolver error / NXDOMAIN) are kept until TTL too, so the detector does not hammer a slow resolver on a known-bad IP. Entries are capped at maxSize; the oldest-by-cachedAt entry is evicted on insert once the cap is reached.
func NewRDNSCache ¶
func NewRDNSCache(cfg RDNSCacheConfig) *RDNSCache
NewRDNSCache returns a ready cache.
func (*RDNSCache) Lookup ¶
Lookup returns the cached hostname for ip, or "" on miss/error/deadline. Lookup blocks the caller for at most cfg.ResolveDeadline; cache hits return immediately.
func (*RDNSCache) QueueStatuses ¶
QueueStatuses reads memory only. Without a deadline, Lookup is synchronous and does not use this bounded resolver pool.
type RDNSCacheConfig ¶
type RDNSCacheConfig struct {
TTL time.Duration
Resolve func(ip net.IP) (string, error)
ResolveDeadline time.Duration
MaxSize int
// MaxConcurrent caps deadline-bound lookups, including returned results
// still owned by their callers. A goroutine blocked on a wedged resolver cannot be
// cancelled in Go, so without a cap a burst of distinct IPs under deadline
// saturation spawns one abandonable goroutine per IP. 0 falls back to
// rdnsCacheDefaultMaxConcurrent.
MaxConcurrent int
}
RDNSCacheConfig is the config block for NewRDNSCache. Resolve is the function used to perform the actual reverse lookup; production callers wrap net.LookupAddr. ResolveDeadline bounds each lookup; 0 disables the deadline. MaxSize caps the number of cached entries to keep memory bounded on hosts that see a wide spread of remote IPs (BPF SMTP-egress is the motivating case); the oldest entry by cachedAt is evicted before a new one is inserted past the cap. 0 falls back to rdnsCacheDefaultMaxSize.
type RemediationResult ¶
type RemediationResult struct {
Success bool `json:"success"`
Action string `json:"action"` // human-readable description of what was done
Description string `json:"description"` // what fix was applied
Error string `json:"error,omitempty"`
// Refused distinguishes an unchanged, ineligible target from an I/O failure.
Refused bool `json:"-"`
// RemediationStatus lets a caller that supports more than one successful
// disposition distinguish an in-place clean from whole-file quarantine.
// It is transport metadata, not part of the generic remediation API.
RemediationStatus string `json:"-"`
// Reverted marks a virtual patch that had to be written again because
// something removed or damaged CSM's earlier block -- typically a backup
// plugin rewriting the .htaccess it owns.
Reverted bool `json:"reverted,omitempty"`
}
RemediationResult describes the outcome of a fix action.
func ApplyFix ¶
func ApplyFix(ctx context.Context, checkType, message, details string, filePath ...string) RemediationResult
ApplyFix executes the remediation action for a finding.
func CleanHtaccessFile ¶
func CleanHtaccessFile(path string) RemediationResult
CleanHtaccessFile audits the file, computes the removal range set, backs up the original, and writes the trimmed content. Returns success=false with no Action when no detector matched (i.e., nothing to clean).
Caller is responsible for gating on cfg.AutoResponse.CleanHtaccess before invoking; this function will clean unconditionally if detectors find anything.
func FixDisableWPCron ¶
func FixDisableWPCron(path string, opts WPCronFixOptions) RemediationResult
FixDisableWPCron disables WP-Cron in a wp-config.php and installs a real per-user system cron that runs wp-cron.php on a fixed interval. It scopes writes to the default per-account roots (/home).
func FixDisableWPCronInRoots ¶
func FixDisableWPCronInRoots(path string, allowedRoots []string, opts WPCronFixOptions) RemediationResult
FixDisableWPCronInRoots is FixDisableWPCron with caller-supplied roots so the Web UI can honor configured account_roots and tests can write under t.TempDir().
func FixDisplayErrorsOn ¶
func FixDisplayErrorsOn(path string) RemediationResult
FixDisplayErrorsOn rewrites an INI / .htaccess / .user.ini file so the display_errors directive is set to Off. The original line is preserved commented out for operator review; an override line is appended at the end of the file so the last-write-wins semantics of every supported config format land on Off regardless of earlier statements.
Only .user.ini, php.ini, and .htaccess are accepted. wp-config.php and other PHP source files require code-level edits this routine does not attempt. The caller (web UI) should not advertise the fix for those.
func FixDisplayErrorsOnInRoots ¶
func FixDisplayErrorsOnInRoots(path string, allowedRoots []string) RemediationResult
FixDisplayErrorsOnInRoots is FixDisplayErrorsOn with caller-supplied roots. The Web UI uses this to honor account_roots outside /home.
func FixErrorLogBloat ¶
func FixErrorLogBloat(path string) RemediationResult
FixErrorLogBloat truncates an account-owned error_log file in place. Truncating preserves the inode and file ownership so any PHP process holding the descriptor keeps appending to the same file without an open/reopen race; this is also the safest action because nothing in the host needs the historical lines to keep serving traffic.
func FixErrorLogBloatInRoots ¶
func FixErrorLogBloatInRoots(path string, allowedRoots []string) RemediationResult
FixErrorLogBloatInRoots is FixErrorLogBloat with caller-supplied roots. The Web UI uses this to include configured account_roots while tests can keep writes under t.TempDir().
func QuarantineFindingFile ¶
func QuarantineFindingFile(f alert.Finding) (RemediationResult, bool)
QuarantineFindingFile quarantines the file a malware/webshell finding points at, for the full-scan --quarantine path. It reuses fixQuarantine (move to the quarantine dir + .meta sidecar) and deliberately covers ONLY the pure file-quarantine check set — it never kills processes, cleans databases, or touches the firewall. Returns eligible=false for any finding that is not a quarantinable malware/webshell FILE finding (caller marks those "left_for_review").
The job runs unattended, so it gets the same bar the scheduled auto-response applies: only a Critical finding (two converging indicators) may act, only on a regular file that is not a symlink, never on a whole directory, and a WordPress core, plugin or theme file is cleaned in place rather than moved, because moving it takes the site down while only the injected code had to go.
func VirtualPatchExposedFile ¶
func VirtualPatchExposedFile(filePath string) RemediationResult
VirtualPatchExposedFile writes an .htaccess "Require all denied" rule that blocks HTTP download of a confirmed web-exposed file without modifying the file itself. For an archive inside a known backup-plugin directory the whole directory is denied. Returns Success=false with an "already" error when all applicable rules are already present (idempotent no-op).
type ReverifySweepStats ¶
type ReverifySweepStats struct {
Considered int
Cleared int
Demoted int
Promoted int
Unchecked int
// TopUncheckedReason is the most common reason a finding could not be
// re-checked at all, which is where a silent sweep usually goes wrong.
TopUncheckedReason string
}
ReverifyStaleFindingsContext is the cancellable form used by the daemon so a large exposure queue cannot delay shutdown for every remaining probe. The bool is false after cancellation so the daemon leaves the sweep version uncommitted and retries it on the next start. ReverifySweepStats describes what a sweep actually did. A sweep that changed nothing is otherwise silent, which makes "ran and found nothing" indistinguishable from "never ran" and from "could not check a single finding" -- the difference an operator needs when findings are not draining.
type SeccompCoverageSummary ¶
type SeccompCoverageSummary struct {
Covered []string // existing units with the CSM drop-in
Uncovered []string // existing units without the drop-in
NotInstalled []string // candidate units not registered with systemd
}
SeccompCoverageSummary collapses the per-unit scan into the two numbers an operator cares about: how many existing units have the CSM drop-in, and how many do not.
func SummarizeAFAlgSeccompCoverage ¶
func SummarizeAFAlgSeccompCoverage() SeccompCoverageSummary
SummarizeAFAlgSeccompCoverage rolls up ScanAFAlgSeccompState into the three-way summary above. Used by the hardening audit and the CLI status output.
type SeccompUnitState ¶
type SeccompUnitState struct {
Unit string // e.g. "lshttpd.service"
Exists bool // unit is registered with systemd on this host
HasFile bool // CSM-managed drop-in is present on disk
}
SeccompUnitState describes one unit's mitigation status in operator terms. Returned by ScanAFAlgSeccompState so both the CLI and the hardening audit can render the same view without re-deriving it.
func ScanAFAlgSeccompState ¶
func ScanAFAlgSeccompState() []SeccompUnitState
ScanAFAlgSeccompState walks the candidate unit list and returns one SeccompUnitState per candidate. Units missing from systemd are still reported (Exists=false) so the operator can confirm CSM did not silently skip something they expected.
type SensitiveFileState ¶
SensitiveFileState is the stable identity of one watchset path. Regular-file inode churn is deliberately excluded, while security metadata and symlink targets remain visible.
type ThreatDB ¶
type ThreatDB struct {
// Stats for WebUI
PermanentCount int
FeedIPCount int
FeedNetCount int
LastFeedUpdate time.Time
LastUpdated time.Time // tracks when feeds were last successfully loaded
// contains filtered or unexported fields
}
ThreatDB is a local IP reputation database built from: 1. CSM's own block history (permanent) 2. Public threat intelligence feeds (updated daily) 3. AbuseIPDB as fallback for unknown IPs
func InitThreatDB ¶
InitThreatDB initializes the global threat database.
func (*ThreatDB) AddOperatorTemporary ¶
AddOperatorTemporary records a timed operator block (the Web UI 24h block) for the lifetime of its firewall block. The evidence is operator-sourced but lapses with the block, so a mistaken 24h block of a customer address does not leave it permanently malicious.
func (*ThreatDB) AddPermanent ¶
AddPermanent adds an IP to the permanent local blocklist. Called for deliberate operator blocks - persists across restarts and never expires. Upgrades an existing temp entry to permanent.
func (*ThreatDB) AddTemporary ¶
AddTemporary records an auto-blocked IP for the lifetime of its firewall block. Unlike AddPermanent the entry lapses with the block: a permanent record turned every temporary auto-block into a forever "known malicious IP" that re-flagged (and re-blocked) the address on each later access. ttl <= 0 is ignored because auto-block evidence must never become a never-expiring threat row.
func (*ThreatDB) AddWhitelist ¶
AddWhitelist adds an IP to the permanent whitelist.
func (*ThreatDB) FeedsStale ¶
FeedsStale returns true if threat feeds have not been updated in over 7 days.
func (*ThreatDB) IsConfigWhitelisted ¶
IsConfigWhitelisted reports whether ip is managed by reputation.whitelist. Runtime removal must not claim to remove these entries because the config remains authoritative and restores them on reload.
func (*ThreatDB) LastFeedRefresh ¶
LastFeedRefresh returns when feeds last loaded successfully, preferring the in-memory timestamp over the persisted one. Zero means never.
func (*ThreatDB) Lookup ¶
Lookup checks if an IP is in the local threat database. Returns (source, true) if found, ("", false) if unknown. Whitelisted IPs always return false.
func (*ThreatDB) LookupMatch ¶
func (db *ThreatDB) LookupMatch(ip string) (ThreatMatch, bool)
LookupMatch is Lookup plus the lifetime of the matched evidence, so the Web UI can explain why an unblocked IP still scores as malicious.
func (*ThreatDB) PruneExpiredThreats ¶
PruneExpiredThreats removes threat entries whose temp lifetime has lapsed, in memory and in the persistent store (which also drops legacy no-source auto-block rows written before expiry tagging existed). Called periodically from the daemon heartbeat; this is by-design lifecycle cleanup, not data retention, so it does not sit behind the opt-in retention sweeps.
func (*ThreatDB) PruneExpiredWhitelist ¶
PruneExpiredWhitelist removes expired temporary whitelist entries. Called periodically from the daemon heartbeat.
func (*ThreatDB) RemovePermanent ¶
RemovePermanent removes an IP from the permanent blocklist and in-memory DB.
func (*ThreatDB) RemoveTemporary ¶
RemoveTemporary removes evidence that only lives as long as a firewall block: auto-block rows and timed operator blocks. It leaves permanent evidence untouched and restores feed ownership when the IP is independently present in a threat feed.
func (*ThreatDB) RemoveWhitelist ¶
RemoveWhitelist removes an operator-managed whitelist entry. Config-managed entries are replaced only by SetConfigWhitelist.
func (*ThreatDB) SetConfigWhitelist ¶
SetConfigWhitelist replaces the entries that come from reputation.whitelist in csm.yaml. Called on config reload; the hot-reload path reported success for that field while lookups kept honouring the startup list. Operator- managed entries added at runtime are untouched.
func (*ThreatDB) TempWhitelist ¶
TempWhitelist adds an IP to the whitelist with a TTL.
func (*ThreatDB) UpdateFeeds ¶
UpdateFeeds downloads fresh threat intelligence feeds. Downloads outside the lock, then swaps data under lock to avoid blocking lookups. Each feed is swapped independently: a feed that fails to download (or fails validation) keeps its previously loaded IPs and CIDRs, so a transient outage never wipes that feed's coverage. lastUpdate only advances when at least one feed succeeded, otherwise the next cycle would skip the retry and serve zero feed data for the whole 20h window.
func (*ThreatDB) WhitelistedIPs ¶
func (db *ThreatDB) WhitelistedIPs() []WhitelistIP
type ThreatMatch ¶
type ThreatMatch struct {
Source string
Permanent bool
ExpiresAt time.Time // zero unless the entry lapses
}
ThreatMatch describes why an IP is flagged: which source named it, and whether that evidence is permanent (re-flags the address on every future sighting) or lapses with the block that recorded it.
type ValiasEntry ¶
ValiasEntry is one destination of one valiases alias.
func ParseValiasEntries ¶
func ParseValiasEntries(r io.Reader, fileDomain string) ([]ValiasEntry, error)
ParseValiasEntries reads a valiases file for fileDomain and returns one entry per destination. cPanel keys aliases by full address ("bob@example.com"); a bare key ("bob", "*") belongs to fileDomain.
type VerifyInput ¶
type VerifyInput struct {
Check, Message, Details, Path string
ContentSHA256, DetectLogic string
Context context.Context
// contains filtered or unexported fields
}
VerifyInput carries everything a finding verifier may need. ContentSHA256 and DetectLogic are populated only for content findings emitted with a fingerprint. Context is optional; long-running verifiers use Background when it is nil.
type VerifyResult ¶
type VerifyResult struct {
Checked bool `json:"checked"`
Resolved bool `json:"resolved"`
// Demote marks a finding whose flagged content is gone but whose
// remediation cannot be proven, because the file changed since detection.
// It is never cleared -- an attacker must not retire a finding by editing
// the file -- but it stops ranking beside live threats.
Demote bool `json:"demote"`
Detail string `json:"detail"`
}
VerifyResult reports whether a finding's underlying condition still holds.
Checked is false when the finding's check type has no cheap, reliable single-target re-check (the caller should tell the operator to dismiss after manual review or run a full account scan). When Checked is true, Resolved reports whether the condition is gone and the finding can be cleared.
func VerifyFinding ¶
func VerifyFinding(checkType, message, details string, filePath ...string) VerifyResult
VerifyFinding re-evaluates a finding by check type + message/details/path. Preserved signature for CLI/legacy callers; carries no content fingerprint.
func VerifyFindingInput ¶
func VerifyFindingInput(in VerifyInput) VerifyResult
VerifyFindingInput re-evaluates a finding from a full VerifyInput. Callers that verify content findings must provide the stored detection fingerprint.
type WPCronFixOptions ¶
type WPCronFixOptions struct {
// IntervalMinutes is how often the installed system cron runs wp-cron.php.
// Clamped to [1,60]; a non-positive value falls back to the 15-minute default.
IntervalMinutes int
// PHPBin is the interpreter the cron line invokes. Empty uses an unambiguous
// cPanel vhost version, then LookPath("php"), then /usr/local/bin/php.
PHPBin string
}
WPCronFixOptions carries operator-tunable parameters for the WP-Cron remediation. Both the Web UI handler and the daemon auto-response resolve these from config before calling the fix, so the remediation core itself stays free of config coupling.
Source Files
¶
- account_enum.go
- account_owner.go
- account_owner_export.go
- account_roots.go
- account_scan.go
- account_scan_truncated.go
- account_scope.go
- af_alg_audit.go
- af_alg_enforce.go
- af_alg_kernel.go
- af_alg_seccomp.go
- apache_config.go
- applyblock.go
- auth.go
- autoblock.go
- autoblock_cleanup_queue.go
- autoblock_netblock.go
- autoblock_queue_health.go
- autoblock_retry_queue.go
- autoresponse.go
- bad_asn_outbound.go
- blockmetrics.go
- bruteforce.go
- challenge_route.go
- challenge_stats.go
- check_completion.go
- check_dispatch_health.go
- check_dispatch_progress.go
- check_execution.go
- check_execution_health.go
- clean.go
- clean_actionlog.go
- cms_config.go
- cmscache.go
- connections.go
- content_reverify_sweep.go
- content_version.go
- correlation.go
- correlation_policy.go
- correlation_report.go
- cpanel_logins.go
- credential_reuse.go
- cron_spool.go
- crontab_fix.go
- crontabs.go
- db_admin_overlap.go
- db_autoresponse.go
- db_clean.go
- db_objects.go
- db_objects_drop.go
- db_options_new_script.go
- db_options_plugin_notice.go
- dbscan.go
- dbscan_cloak_config.go
- dbscan_cms_common.go
- dbscan_drupal.go
- dbscan_filters.go
- dbscan_hidden_links.go
- dbscan_joomla.go
- dbscan_magento.go
- dbscan_opencart.go
- dbscan_phantom_authors.go
- dbscan_post_burst.go
- dbscan_query_failure.go
- dbscan_siteurl_host.go
- dbscan_spam_taxonomy.go
- dbscan_stored_cloak.go
- dbscan_stored_code.go
- direct_smtp_egress.go
- direct_smtp_egress_metrics.go
- dns.go
- dns_ssl.go
- email_hash_queue.go
- email_password_hash.go
- emailpasswd.go
- emailscan.go
- exfiltration.go
- exposed_files.go
- exposed_files_archive.go
- exposed_files_verify.go
- exposed_repo.go
- exposed_virtual_patch.go
- exposed_virtual_patch_commit_linux.go
- exposed_virtual_patch_restore.go
- file_response.go
- fileindex.go
- fileindex_queue_health.go
- fileindex_uploads_score.go
- filesystem.go
- firewall.go
- forwarder.go
- ftp_fail_tracker.go
- fullscan_quarantine.go
- hardening.go
- hardening_audit.go
- health.go
- helpers.go
- htaccess_bounded_read.go
- htaccess_clean.go
- htaccess_shield_gates.go
- http_abuse.go
- http_asn_crawl.go
- identquote.go
- ip_response_policy.go
- jstaint_adapter.go
- jstaint_deep.go
- kill_identity.go
- local_threat.go
- login_identity.go
- mail.go
- mailfilter.go
- mailrate.go
- mailsieve.go
- network.go
- perf_remediate.go
- performance.go
- phishing.go
- php_content.go
- php_content_cursors.go
- php_content_rolling.go
- php_translation_cache.go
- php_wp_version.go
- phpconfig.go
- phpexec.go
- phptaint_adapter.go
- phptaint_deep.go
- plugin_queue_health.go
- plugin_refresh_cycle.go
- plugincheck.go
- postfix_audit.go
- processes.go
- provider.go
- quarantine_hardlink_linux.go
- quarantine_metadata.go
- quarantine_name.go
- quarantine_safe.go
- quarantine_safe_common.go
- quarantine_transaction.go
- rdns_cache.go
- rdns_queue.go
- registry.go
- remediate.go
- reputation.go
- reputation_queue_health.go
- rolling_coverage.go
- runner.go
- scan_batch_health.go
- scan_options.go
- self_write.go
- self_write_identity_linux.go
- sensitive_file_ownership_linux.go
- sensitive_files.go
- sensitive_files_provenance.go
- sensitive_wpcron.go
- sitedetect.go
- socket_owner.go
- spam_context.go
- ssh_logins_follow.go
- supplychain.go
- syslog_time.go
- system.go
- taint_oversize_gate.go
- threatfeeds.go
- timthumb.go
- uidcache.go
- url_reputation.go
- user_ini_signature.go
- varfunc_indirection.go
- verify.go
- verify_db.go
- verify_db_admin.go
- verify_db_cms.go
- verify_db_objects.go
- verify_pkg.go
- verify_system.go
- verify_wp.go
- vuln_plugins.go
- vuln_plugins_unprotected.go
- waf.go
- waf_modsec_disabled.go
- waf_reload.go
- web.go
- whm.go
- wp_verification.go
- wpcore_queue_health.go
- wpcron_migrate.go
- wpcron_phpbin.go
- wpcron_remediate.go
- wpinstalls.go
- yara_deep.go
- yara_deep_gaps.go