This commit is contained in:
David Schroeder
2026-07-25 15:57:12 -05:00
parent 90d5b637ac
commit b20f14e32d
7 changed files with 60 additions and 26 deletions
+12 -4
View File
@@ -11,7 +11,7 @@ func TestAuditFiltersFromRequest(t *testing.T) {
t.Parallel()
request := httptest.NewRequest(
"GET",
"/portal?audit_range=7d&audit_event=package_downloaded&audit_customer=Acme&audit_actor=taiadmin&audit_hostname=pve01&audit_package=sentinelone-linux&audit_ip=10.10.1.25&audit_details=install-rmm",
"/portal?audit_range=7d&audit_event=package_downloaded&audit_customer=Acme&audit_user=taiadmin&audit_hostname=pve01&audit_package=sentinelone-linux&audit_ip=10.10.1.25&audit_details=install-rmm",
nil,
)
filters := auditFiltersFromRequest(request)
@@ -19,7 +19,7 @@ func TestAuditFiltersFromRequest(t *testing.T) {
TimeRange: "7d",
EventType: "package_downloaded",
CustomerLabel: "Acme",
Actor: "taiadmin",
User: "taiadmin",
Hostname: "pve01",
PackageSlug: "sentinelone-linux",
SourceIP: "10.10.1.25",
@@ -38,6 +38,14 @@ func TestAuditFiltersDefaultToThirtyDays(t *testing.T) {
}
}
func TestAuditFiltersAcceptLegacyActorParameter(t *testing.T) {
t.Parallel()
request := httptest.NewRequest("GET", "/portal?audit_actor=legacy-user", nil)
if filters := auditFiltersFromRequest(request); filters.User != "legacy-user" {
t.Fatalf("user = %q, want legacy-user", filters.User)
}
}
func TestHasAuditQuery(t *testing.T) {
t.Parallel()
if !hasAuditQuery(httptest.NewRequest("GET", "/portal?audit_hostname=pve01", nil)) {
@@ -54,7 +62,7 @@ func TestAuditQueryUsesPlaceholders(t *testing.T) {
TimeRange: "all",
EventType: "package_downloaded",
CustomerLabel: "Acme",
Actor: "taiadmin",
User: "taiadmin",
Hostname: "pve01",
PackageSlug: "sentinelone-linux",
SourceIP: "10.10.1.25",
@@ -62,7 +70,7 @@ func TestAuditQueryUsesPlaceholders(t *testing.T) {
}
query, arguments := auditQuery(filters, 250)
if strings.Contains(query, filters.CustomerLabel) ||
strings.Contains(query, filters.Actor) ||
strings.Contains(query, filters.User) ||
strings.Contains(query, filters.Hostname) {
t.Fatal("filter values must not be interpolated into the SQL query")
}
+11 -5
View File
@@ -62,6 +62,7 @@ func hasAuditQuery(r *http.Request) bool {
"audit_range",
"audit_event",
"audit_customer",
"audit_user",
"audit_actor",
"audit_hostname",
"audit_package",
@@ -155,11 +156,16 @@ func auditFiltersFromRequest(r *http.Request) auditFilters {
default:
timeRange = "30d"
}
userFilter := query.Get("audit_user")
if strings.TrimSpace(userFilter) == "" {
// Preserve audit links created before the portal used "user" terminology.
userFilter = query.Get("audit_actor")
}
return auditFilters{
TimeRange: timeRange,
EventType: limitedFilter(query.Get("audit_event"), 100),
CustomerLabel: limitedFilter(query.Get("audit_customer"), 255),
Actor: limitedFilter(query.Get("audit_actor"), 255),
User: limitedFilter(userFilter, 255),
Hostname: limitedFilter(query.Get("audit_hostname"), 255),
PackageSlug: limitedFilter(query.Get("audit_package"), 100),
SourceIP: limitedFilter(query.Get("audit_ip"), 64),
@@ -224,7 +230,7 @@ func auditQuery(filters auditFilters, limit int) (string, []any) {
value string
}{
{"COALESCE(a.customer_label, '')", filters.CustomerLabel},
{"ae.actor", filters.Actor},
{"ae.actor", filters.User},
{"ae.hostname", filters.Hostname},
{"ae.package_slug", filters.PackageSlug},
{"ae.source_ip", filters.SourceIP},
@@ -260,7 +266,7 @@ func (s *Server) listAuditEvents(r *http.Request, filters auditFilters, limit in
if err := rows.Scan(
&record.EventType,
&record.CustomerLabel,
&record.Actor,
&record.User,
&record.Hostname,
&record.PackageSlug,
&record.SourceIP,
@@ -606,7 +612,7 @@ func validSHA256(value string) bool {
func (s *Server) audit(
ctx context.Context,
eventType string,
actor string,
user string,
authorizationID *uint64,
hostname string,
packageSlug string,
@@ -622,7 +628,7 @@ func (s *Server) audit(
`INSERT INTO audit_events
(event_type, actor, authorization_id, hostname, package_slug, source_ip, details)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
eventType, actor, authID, hostname, packageSlug, sourceIP, details,
eventType, user, authID, hostname, packageSlug, sourceIP, details,
)
return err
}
+2 -2
View File
@@ -71,7 +71,7 @@ type authorizationRecord struct {
type auditRecord struct {
EventType string
CustomerLabel string
Actor string
User string
Hostname string
PackageSlug string
SourceIP string
@@ -83,7 +83,7 @@ type auditFilters struct {
TimeRange string
EventType string
CustomerLabel string
Actor string
User string
Hostname string
PackageSlug string
SourceIP string
+1 -1
View File
@@ -39,7 +39,7 @@
</select>
</label>
<label>Customer / deployment <input name="audit_customer" value="{{.AuditFilters.CustomerLabel}}" placeholder="Acme cluster refresh"></label>
<label>Technician / actor <input name="audit_actor" value="{{.AuditFilters.Actor}}" placeholder="taiadmin"></label>
<label>User <input name="audit_user" value="{{.AuditFilters.User}}" placeholder="taiadmin"></label>
<label>Hostname <input name="audit_hostname" value="{{.AuditFilters.Hostname}}" placeholder="pve01"></label>
<label>Package ID <input name="audit_package" value="{{.AuditFilters.PackageSlug}}" placeholder="sentinelone-linux"></label>
<label>Source IP <input name="audit_ip" value="{{.AuditFilters.SourceIP}}" placeholder="10.10.1.25"></label>
+2 -2
View File
@@ -28,7 +28,7 @@
<div class="audit-row audit-header" aria-hidden="true">
<span>Event</span>
<span>Customer / deployment</span>
<span>Actor / host / package</span>
<span>User / host / package</span>
<span>Source / details</span>
</div>
{{range .AuditEvents}}
@@ -41,7 +41,7 @@
{{if .CustomerLabel}}<span>{{.CustomerLabel}}</span>{{else}}<span></span>{{end}}
</div>
<div>
{{if .Actor}}<span>{{.Actor}}</span>{{end}}
{{if .User}}<span>{{.User}}</span>{{end}}
{{if .Hostname}}<span>{{.Hostname}}</span>{{end}}
{{if .PackageSlug}}<span>{{.PackageSlug}}</span>{{end}}
</div>