mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* feat: Add cloud sync command * feat: Add pmg cloud commands * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes * fix: Code review fixes
279 lines
7.1 KiB
Go
279 lines
7.1 KiB
Go
package audit
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
|
"github.com/safedep/dry/log"
|
|
"github.com/safedep/pmg/config"
|
|
"github.com/safedep/pmg/internal/analytics"
|
|
)
|
|
|
|
var global *auditor
|
|
|
|
func setGlobal(a *auditor) {
|
|
global = a
|
|
}
|
|
|
|
func resetGlobal() {
|
|
global = nil
|
|
}
|
|
|
|
// 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())
|
|
|
|
if cfg.Config.Cloud.Enabled && !analytics.IsDisabled() {
|
|
cs, err := newCloudSink(cfg)
|
|
if err != nil {
|
|
log.Warnf("Cloud sync initialization failed: %v", err)
|
|
} else {
|
|
sinks = append(sinks, cs)
|
|
}
|
|
}
|
|
|
|
if cfg.Config.Cloud.Enabled && analytics.IsDisabled() {
|
|
log.Warnf("Cloud sync is disabled because telemetry is disabled")
|
|
}
|
|
|
|
setGlobal(newAuditor(sinks...))
|
|
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, analysisID string, isMalware, isVerified bool) {
|
|
logEvent(AuditEvent{
|
|
Type: EventTypeMalwareConfirmed,
|
|
Message: fmt.Sprintf("User confirmed installation of flagged package: %s@%s", pkgName(pv), pkgVersion(pv)),
|
|
PackageVersion: pv,
|
|
AnalysisID: analysisID,
|
|
IsMalware: isMalware,
|
|
IsVerified: isVerified,
|
|
})
|
|
|
|
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)
|
|
}
|
|
|
|
// 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()
|
|
|
|
logEvent(AuditEvent{
|
|
Type: EventTypeSessionComplete,
|
|
Message: fmt.Sprintf("Session complete: %s", outcome),
|
|
SessionData: &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,
|
|
Duration: time.Since(s.startTime),
|
|
SandboxEnabled: cfg.Config.Sandbox.Enabled,
|
|
ParanoidMode: cfg.Config.Paranoid,
|
|
TransitiveEnabled: cfg.Config.Transitive,
|
|
},
|
|
})
|
|
}
|
|
|
|
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
|
|
}
|