Skip to content

Commit

Permalink
feat: lumberjack log validation (#428)
Browse files Browse the repository at this point in the history
* feat: lumberjack log validation

* rename package

* address comment

* fix lint

* fix nits
  • Loading branch information
sqin2019 authored Aug 2, 2023
1 parent 85279e6 commit 3e5ca59
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 0 deletions.
58 changes: 58 additions & 0 deletions pkg/validation/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2023 The Authors (see AUTHORS file)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package validation provides untils for lumberjack/data access logs
// validation.
package validation

import (
"fmt"

"google.golang.org/protobuf/encoding/protojson"

lepb "cloud.google.com/go/logging/apiv2/loggingpb"
cal "google.golang.org/genproto/googleapis/cloud/audit"
)

// Validate validates a json string representation of a lumberjack log.
func Validate(log string) error {
var logEntry lepb.LogEntry
if err := protojson.Unmarshal([]byte(log), &logEntry); err != nil {
return fmt.Errorf("failed to parse log entry as JSON: %w", err)
}

if err := validatePayload(&logEntry); err != nil {
return fmt.Errorf("failed to validate payload: %w", err)
}

// TODO (#427): add required fields check.
return nil
}

func validatePayload(logEntry *lepb.LogEntry) error {
payload := logEntry.GetJsonPayload()
if payload == nil {
return fmt.Errorf("missing audit log payload")
}

var al cal.AuditLog
val, err := payload.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to extract audit log from JSON payload: %w", err)
}
if err := protojson.Unmarshal(val, &al); err != nil {
return fmt.Errorf("failed to parse JSON payload: %w", err)
}
return nil
}
111 changes: 111 additions & 0 deletions pkg/validation/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2023 The Authors (see AUTHORS file)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package validation

import (
"testing"

pkgtestutil "github.com/abcxyz/pkg/testutil"
)

const validLog = `
{
"insertId": "foo",
"jsonPayload": {
"metadata": {
"originating_resource": {
"labels": {
"service_name": "foo_service",
"project_id": "foo_project",
"revision_name": "foo_revision",
"configuration_name": "foo_configuration",
"location": "us-central1"
},
"type": "foo_type"
}
},
"request": {
"foo": "bar",
"trace_id": "foo_trace_id"
},
"service_name": "foo_service",
"method_name": "foo_method",
"resource_name": "foo_resource",
"authentication_info": {
"principal_email": "foo@bet.com"
}
},
"resource": {
"type": "foo_type",
"labels": {
"project_id": "foo_project",
"configuration_name": "foo_configuration_name",
"service_name": "foo_service",
"location": "us-central1",
"revision_name": "foo_revision"
}
},
"timestamp": "2022-01-19T22:05:15.687616341Z",
"labels": {
"trace_id": "foo_trace_id",
"accessing_process_family": "foo-process-family",
"accessing_process_name": "foo-process",
"accessed_data_type": "foo-customer-info"
},
"logName": "projects/foo_project/logs/auditlog.gcloudsolutions.dev%2Fdata_access",
"receiveTimestamp": "2022-01-19T22:05:15.706507037Z"
}`

func TestValidate(t *testing.T) {
t.Parallel()

tests := []struct {
name string
log string
wantErrSubstr string
}{
{
name: "success",
log: validLog,
},
{
name: "invalid_log",
log: `invalid`,
wantErrSubstr: "failed to parse log entry as JSON",
},
{
name: "invalid_log_payload",
log: `
{
"jsonPayload": {
"invalidKey": "foo"
}
}`,
wantErrSubstr: "failed to parse JSON payload",
},
}
for _, tc := range tests {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

err := Validate(tc.log)
if diff := pkgtestutil.DiffErrString(err, tc.wantErrSubstr); diff != "" {
t.Errorf("Process(%+v) got unexpected error substring: %v", tc.name, diff)
}
})
}
}

0 comments on commit 3e5ca59

Please sign in to comment.