Files
pmg/internal/audit/eventlog_sink.go
T
Sahil BansalGitHubClaude Opus 4.6devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>Abhisek Dattadevin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
19d735cdd2 feat: emit cloud events for dependency cooldown and host observations (#243)
* feat: emit cloud events for dependency cooldown and host observations (#237)

Wire cooldown blocks and proxy host observations through the cloud sync
pipeline so they appear as telemetry in Control Tower.

- Cooldown blocks emit PACKAGE_DECISION with COOLDOWN_BLOCKED action and
  PmgDependencyCooldown context (publish date, cooldown days, days since
  publish, days remaining)
- Proxy host observations emit HOST_OBSERVATION with PmgHostObservation
  (hostname, method)
- Session summary now includes cooldown_blocked_count
- Updated buf API dependency for new proto schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* format file

* fix: add explicit eventlog mapping for EventTypeDependencyCooldown

Follow the existing pattern where every audit event type has an explicit
case in mapEventType and a corresponding constant in the eventlog package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Apply suggestion from @devin-ai-integration[bot]

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>

---------

Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-08 10:01:25 +05:30

82 lines
2.3 KiB
Go

package audit
import (
"context"
"github.com/safedep/pmg/internal/eventlog"
)
type eventlogSink struct{}
func newEventlogSink() *eventlogSink {
return &eventlogSink{}
}
func (s *eventlogSink) Handle(_ context.Context, event AuditEvent) error {
details := event.Details
if details == nil && event.SessionData != nil {
details = sessionDataToDetails(event.SessionData)
}
elEvent := eventlog.Event{
Timestamp: event.Timestamp,
EventType: mapEventType(event.Type),
Message: event.Message,
PackageName: pkgName(event.PackageVersion),
Version: pkgVersion(event.PackageVersion),
Ecosystem: pkgEcosystem(event.PackageVersion),
Details: details,
}
return eventlog.LogEvent(elEvent)
}
func sessionDataToDetails(sd *SessionData) map[string]interface{} {
return map[string]interface{}{
"outcome": sd.Outcome,
"flow_type": sd.FlowType,
"package_manager": sd.PackageManager,
"total_analyzed": sd.TotalAnalyzed,
"allowed_count": sd.AllowedCount,
"blocked_count": sd.BlockedCount,
"confirmed_count": sd.ConfirmedCount,
"trusted_skipped": sd.TrustedSkipped,
"insecure_bypassed": sd.InsecureBypassed,
"cooldown_blocked_count": sd.CooldownBlockedCount,
}
}
func (s *eventlogSink) Close() error {
return nil
}
func mapEventType(t EventType) eventlog.EventType {
switch t {
case EventTypeMalwareBlocked:
return eventlog.EventTypeMalwareBlocked
case EventTypeMalwareConfirmed:
return eventlog.EventTypeMalwareConfirmed
case EventTypeInstallAllowed:
return eventlog.EventTypeInstallAllowed
case EventTypeInstallTrustedAllowed:
return eventlog.EventTypeInstallTrustedAllowed
case EventTypeInstallStarted:
return eventlog.EventTypeInstallStarted
case EventTypeDependencyResolved:
return eventlog.EventTypeDependencyResolved
case EventTypeInstallInsecureBypass:
return eventlog.EventTypeInstallInsecureBypass
case EventTypeProxyHostObserved:
return eventlog.EventTypeProxyHostObserved
case EventTypeDependencyCooldown:
return eventlog.EventTypeDependencyCooldown
case EventTypeSandboxOverride:
return eventlog.EventTypeSandboxOverride
case EventTypeError:
return eventlog.EventTypeError
case EventTypeSessionComplete:
return eventlog.EventType("session_complete")
default:
return eventlog.EventType(string(t))
}
}