mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Add audit system with sink based dispatcher (#211)
* chore: Dependency update * feat: Add audit system with eventlog as a sink * fix: Linter fixes * fix: Code review fixes
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||
)
|
||||
|
||||
var global *auditor
|
||||
|
||||
func setGlobal(a *auditor) {
|
||||
global = a
|
||||
}
|
||||
|
||||
func resetGlobal() {
|
||||
global = nil
|
||||
}
|
||||
|
||||
func Initialize() error {
|
||||
a := newAuditor(newEventlogSink())
|
||||
global = a
|
||||
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,
|
||||
Details: map[string]interface{}{
|
||||
"reason": reason,
|
||||
"analysis_id": analysisID,
|
||||
"reference_url": referenceURL,
|
||||
},
|
||||
})
|
||||
|
||||
if global != nil {
|
||||
global.recordBlocked()
|
||||
}
|
||||
}
|
||||
|
||||
// LogMalwareConfirmed records that the user confirmed installation of a flagged package.
|
||||
func LogMalwareConfirmed(pv *packagev1.PackageVersion) {
|
||||
logEvent(AuditEvent{
|
||||
Type: EventTypeMalwareConfirmed,
|
||||
Message: fmt.Sprintf("User confirmed installation of flagged package: %s@%s", pkgName(pv), pkgVersion(pv)),
|
||||
PackageVersion: pv,
|
||||
})
|
||||
|
||||
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,
|
||||
Details: map[string]interface{}{
|
||||
"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),
|
||||
Details: map[string]interface{}{
|
||||
"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.
|
||||
func LogProxyHostObserved(hostname, method, reason string, details map[string]interface{}) {
|
||||
base := map[string]interface{}{
|
||||
"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,
|
||||
})
|
||||
}
|
||||
|
||||
// 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)),
|
||||
Details: map[string]interface{}{
|
||||
"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 {
|
||||
event.Details = map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
logEvent(event)
|
||||
}
|
||||
|
||||
func mergeDetails(base, extra map[string]interface{}) map[string]interface{} {
|
||||
if base == nil {
|
||||
base = make(map[string]interface{})
|
||||
}
|
||||
for k, v := range extra {
|
||||
base[k] = v
|
||||
}
|
||||
return base
|
||||
}
|
||||
Reference in New Issue
Block a user