2026-04-10 19:07:15 +05:30
package audit
import (
"context"
"fmt"
2026-04-11 12:33:27 +05:30
"time"
2026-04-10 19:07:15 +05:30
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
2026-04-11 12:33:27 +05:30
"github.com/safedep/dry/log"
"github.com/safedep/pmg/config"
2026-04-10 19:07:15 +05:30
)
var global * auditor
func setGlobal ( a * auditor ) {
global = a
}
func resetGlobal () {
global = nil
}
2026-04-11 12:33:27 +05:30
// Initialize sets up the audit system with an eventlog sink and, when enabled,
// a cloud sync sink.
func Initialize ( cfg * config . RuntimeConfig ) error {
var sinks [] Sink
sinks = append ( sinks , newEventlogSink ())
2026-06-17 16:03:26 +05:30
if cfg . Config . Cloud . Enabled {
2026-05-28 19:12:12 +05:30
cs , err := newCloudSink ( cfg , newCloudSinkCIResolver ())
2026-04-11 12:33:27 +05:30
if err != nil {
2026-04-13 13:40:18 +05:30
log . Warnf ( "Cloud sync initialization failed: %v" , err )
} else {
sinks = append ( sinks , cs )
2026-04-11 12:33:27 +05:30
}
}
setGlobal ( newAuditor ( sinks ... ))
2026-04-10 19:07:15 +05:30
return nil
}
func Close () error {
if global == nil {
return nil
}
return global . close ()
}
func logEvent ( event AuditEvent ) {
if global == nil {
return
}
global . dispatch ( context . Background (), event )
}
func pkgName ( pv * packagev1 . PackageVersion ) string {
if pv != nil {
if pkg := pv . GetPackage (); pkg != nil {
return pkg . GetName ()
}
}
return ""
}
func pkgVersion ( pv * packagev1 . PackageVersion ) string {
if pv != nil {
return pv . GetVersion ()
}
return ""
}
func pkgEcosystem ( pv * packagev1 . PackageVersion ) string {
if pv != nil {
if pkg := pv . GetPackage (); pkg != nil {
return pkg . GetEcosystem (). String ()
}
}
return ""
}
// LogMalwareBlocked records that a package was blocked due to malware detection.
func LogMalwareBlocked ( pv * packagev1 . PackageVersion , reason , analysisID , referenceURL string , isMalware , isVerified bool ) {
logEvent ( AuditEvent {
Type : EventTypeMalwareBlocked ,
Message : fmt . Sprintf ( "Blocked installation of malicious package: %s@%s" , pkgName ( pv ), pkgVersion ( pv )),
PackageVersion : pv ,
AnalysisID : analysisID ,
IsMalware : isMalware ,
IsVerified : isVerified ,
2026-06-21 18:22:15 +05:30
Details : map [ string ] any {
2026-04-10 19:07:15 +05:30
"reason" : reason ,
"analysis_id" : analysisID ,
"reference_url" : referenceURL ,
},
})
if global != nil {
global . recordBlocked ()
}
}
// LogMalwareConfirmed records that the user confirmed installation of a flagged package.
2026-04-11 12:33:27 +05:30
func LogMalwareConfirmed ( pv * packagev1 . PackageVersion , analysisID string , isMalware , isVerified bool ) {
2026-04-10 19:07:15 +05:30
logEvent ( AuditEvent {
Type : EventTypeMalwareConfirmed ,
Message : fmt . Sprintf ( "User confirmed installation of flagged package: %s@%s" , pkgName ( pv ), pkgVersion ( pv )),
PackageVersion : pv ,
2026-04-11 12:33:27 +05:30
AnalysisID : analysisID ,
IsMalware : isMalware ,
IsVerified : isVerified ,
2026-04-10 19:07:15 +05:30
})
if global != nil {
global . recordConfirmed ()
}
}
// LogInstallAllowed records that a package passed security checks and installation was permitted.
func LogInstallAllowed ( pv * packagev1 . PackageVersion , packageCount int ) {
logEvent ( AuditEvent {
Type : EventTypeInstallAllowed ,
Message : fmt . Sprintf ( "Installation allowed for %s@%s (%d packages analyzed)" , pkgName ( pv ), pkgVersion ( pv ), packageCount ),
PackageVersion : pv ,
2026-06-21 18:22:15 +05:30
Details : map [ string ] any {
2026-04-10 19:07:15 +05:30
"packages_analyzed" : packageCount ,
},
PackageCount : packageCount ,
})
if global != nil {
global . recordAllowed ()
}
}
// LogInstallTrustedAllowed records that a trusted package skipped security analysis.
func LogInstallTrustedAllowed ( pv * packagev1 . PackageVersion ) {
logEvent ( AuditEvent {
Type : EventTypeInstallTrustedAllowed ,
Message : fmt . Sprintf ( "Installation allowed for trusted package: %s@%s" , pkgName ( pv ), pkgVersion ( pv )),
PackageVersion : pv ,
})
if global != nil {
global . recordTrustedSkipped ()
}
}
// LogInstallInsecureBypass records that a package bypassed security analysis due to insecure mode.
func LogInstallInsecureBypass ( pv * packagev1 . PackageVersion ) {
logEvent ( AuditEvent {
Type : EventTypeInstallInsecureBypass ,
Message : fmt . Sprintf ( "Installation bypassed analysis due to insecure installation mode: %s@%s" , pkgName ( pv ), pkgVersion ( pv )),
PackageVersion : pv ,
})
if global != nil {
global . recordInsecureBypassed ()
}
}
// LogInstallStarted records the start of a package installation session.
func LogInstallStarted ( packageManager string , args [] string ) {
logEvent ( AuditEvent {
Type : EventTypeInstallStarted ,
Message : fmt . Sprintf ( "Starting package installation with %s" , packageManager ),
2026-06-21 18:22:15 +05:30
Details : map [ string ] any {
2026-04-10 19:07:15 +05:30
"package_manager" : packageManager ,
"arguments" : args ,
},
PackageManager : packageManager ,
Args : args ,
})
if global != nil {
global . startSession ( packageManager , args )
}
}
// LogProxyHostObserved records an outbound host observed by the proxy that is not a known registry.
2026-06-21 18:22:15 +05:30
func LogProxyHostObserved ( hostname , method , reason string , details map [ string ] any ) {
base := map [ string ] any {
2026-04-10 19:07:15 +05:30
"hostname" : hostname ,
"method" : method ,
"reason" : reason ,
}
logEvent ( AuditEvent {
Type : EventTypeProxyHostObserved ,
Message : fmt . Sprintf ( "Proxy observed outbound host: %s" , hostname ),
Details : mergeDetails ( base , details ),
Hostname : hostname ,
Method : method ,
Reason : reason ,
})
}
2026-05-08 10:01:25 +05:30
// LogDependencyCooldown records that a package was blocked by the dependency cooldown policy.
func LogDependencyCooldown ( pv * packagev1 . PackageVersion , publishDate time . Time , cooldownDays , daysAgo , daysLeft int ) {
logEvent ( AuditEvent {
Type : EventTypeDependencyCooldown ,
Message : fmt . Sprintf ( "Package blocked by cooldown policy: %s@%s (published %d days ago, %d days remaining)" , pkgName ( pv ), pkgVersion ( pv ), daysAgo , daysLeft ),
PackageVersion : pv ,
PublishDate : publishDate ,
CooldownDays : cooldownDays ,
DaysAgo : daysAgo ,
DaysLeft : daysLeft ,
})
if global != nil {
global . recordCooldownBlocked ()
}
}
2026-06-21 18:22:15 +05:30
// CooldownSkipReason is the only source that produces a dependency_cooldown_skipped
// event; trusted-package exemptions surface as install_trusted_allowed instead.
const CooldownSkipReason = "dependency_cooldown.skip"
// LogCooldownSkipped records that a specific package version was exempted from
// the dependency cooldown window by the dependency_cooldown.skip list.
func LogCooldownSkipped ( pv * packagev1 . PackageVersion ) {
logEvent ( AuditEvent {
Type : EventTypeCooldownSkipped ,
Message : fmt . Sprintf ( "Cooldown skipped for %s@%s" , pkgName ( pv ), pkgVersion ( pv )),
PackageVersion : pv ,
Reason : CooldownSkipReason ,
Details : map [ string ] any {
"reason" : CooldownSkipReason ,
},
})
}
2026-04-10 19:07:15 +05:30
// LogSandboxOverride records that runtime sandbox policy overrides were applied.
func LogSandboxOverride ( sandboxProfile string , overrides [] map [ string ] string ) {
logEvent ( AuditEvent {
Type : EventTypeSandboxOverride ,
Message : fmt . Sprintf ( "Sandbox runtime overrides applied (%d rules)" , len ( overrides )),
2026-06-21 18:22:15 +05:30
Details : map [ string ] any {
2026-04-10 19:07:15 +05:30
"sandbox_profile" : sandboxProfile ,
"sandbox_runtime_overrides" : overrides ,
},
ProfileName : sandboxProfile ,
Overrides : overrides ,
})
}
// LogError records a significant error during PMG operation.
func LogError ( message string , err error ) {
event := AuditEvent {
Type : EventTypeError ,
Message : message ,
Error : err ,
}
if err != nil {
2026-06-21 18:22:15 +05:30
event . Details = map [ string ] any {
2026-04-10 19:07:15 +05:30
"error" : err . Error (),
}
}
logEvent ( event )
}
2026-04-11 12:33:27 +05:30
// LogSessionComplete records the end of a PMG invocation with aggregate session stats.
func LogSessionComplete ( outcome Outcome , flowType FlowType ) {
if global == nil {
return
}
s := global . getSession ()
if s == nil {
return
}
s . mu . Lock ()
defer s . mu . Unlock ()
cfg := config . Get ()
2026-06-26 14:47:53 +05:30
LogSessionSummary ( SessionData {
PackageManager : s . packageManager ,
FlowType : flowType ,
Outcome : outcome ,
TotalAnalyzed : s . totalAnalyzed ,
AllowedCount : s . allowedCount ,
BlockedCount : s . blockedCount ,
ConfirmedCount : s . confirmedCount ,
TrustedSkipped : s . trustedSkipped ,
InsecureBypassed : s . insecureBypassed ,
CooldownBlockedCount : s . cooldownBlockedCount ,
Duration : time . Since ( s . startTime ),
SandboxEnabled : cfg . Config . Sandbox . Enabled ,
ParanoidMode : cfg . Config . Paranoid ,
TransitiveEnabled : cfg . Config . Transitive ,
})
}
// LogSessionSummary emits a session-complete audit event from explicit session
// data. The persistent proxy daemon uses this because it aggregates run stats in
// a stats collector rather than the per-invocation audit session that
// LogSessionComplete reads from.
func LogSessionSummary ( data SessionData ) {
if global == nil {
return
}
2026-04-11 12:33:27 +05:30
logEvent ( AuditEvent {
2026-06-26 14:47:53 +05:30
Type : EventTypeSessionComplete ,
Message : fmt . Sprintf ( "Session complete: %s" , data . Outcome ),
SessionData : & data ,
2026-04-11 12:33:27 +05:30
})
}
2026-06-21 18:22:15 +05:30
func mergeDetails ( base , extra map [ string ] any ) map [ string ] any {
2026-04-10 19:07:15 +05:30
if base == nil {
2026-06-21 18:22:15 +05:30
base = make ( map [ string ] any )
2026-04-10 19:07:15 +05:30
}
for k , v := range extra {
base [ k ] = v
}
return base
}