Skip to content

Commit

Permalink
Implementing json-logger.
Browse files Browse the repository at this point in the history
  • Loading branch information
andream16 committed Dec 2, 2024
1 parent 0c32765 commit bd1db14
Show file tree
Hide file tree
Showing 1,023 changed files with 797,866 additions and 0 deletions.
3 changes: 3 additions & 0 deletions new-components/reporters/json-logger/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SMITHY_INSTANCE_ID=8d719c1c-c569-4078-87b3-4951bd4012ee
SMITHY_LOG_LEVEL=debug
SMITHY_BACKEND_STORE_TYPE=local
25 changes: 25 additions & 0 deletions new-components/reporters/json-logger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# json-logger

This is component implements a [reporter](https://github.com/smithy-security/smithy/blob/main/sdk/component/component.go)
that prints vulnerability findings as json.

## Environment variables

The component used environment variables for configuration.

It requires the component
environment variables defined [here](https://github.com/smithy-security/smithy/blob/main/sdk/README.md#component).

## How to run

Execute:

```shell
docker-compose up --build --force-recreate --remove-orphans
```

Then shutdown with:

```shell
docker-compose down --rmi all
```
35 changes: 35 additions & 0 deletions new-components/reporters/json-logger/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"context"
"fmt"
"log"
"time"

"github.com/smithy-security/smithy/sdk/component"

"github.com/smithy-security/smithy/new-components/reporters/json-logger/internal/reporter"
)

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()

if err := Main(ctx); err != nil {
log.Fatalf("unexpected error: %v", err)
}
}

func Main(ctx context.Context, opts ...component.RunnerOption) error {
opts = append(opts, component.RunnerWithComponentName("json-logger"))

if err := component.RunReporter(
ctx,
reporter.NewJsonLogger(),
opts...,
); err != nil {
return fmt.Errorf("could not run reporter: %w", err)
}

return nil
}
317 changes: 317 additions & 0 deletions new-components/reporters/json-logger/cmd/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
package main

import (
"context"
"log/slog"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"

"github.com/smithy-security/smithy/sdk/component"
ocsf "github.com/smithy-security/smithy/sdk/gen/com/github/ocsf/ocsf_schema/v1"
)

type (
capturingLogger struct {
logs []string
fields []slog.Attr
}

store struct {
vulns []*ocsf.VulnerabilityFinding
}
)

func (s *store) Close(ctx context.Context) error {
return nil
}

func (s *store) Validate(finding *ocsf.VulnerabilityFinding) error {
return nil
}

func (s *store) Read(ctx context.Context, instanceID string) ([]*ocsf.VulnerabilityFinding, error) {
return s.vulns, nil
}

func (s *store) Update(ctx context.Context, instanceID string, findings []*ocsf.VulnerabilityFinding) error {
return nil
}

func (s *store) Write(ctx context.Context, instanceID string, findings []*ocsf.VulnerabilityFinding) error {
return nil
}

func (c *capturingLogger) capture(msg string, keyvals ...any) {
if msg != "" {
c.logs = append(c.logs, msg)
}

if len(keyvals) > 0 {
newAttr := slog.Attr{}
for idx, kv := range keyvals {
attr, ok := kv.(slog.Attr)
if ok {
c.fields = append(c.fields, attr)
continue
}

if idx%2 == 0 {
newAttr.Key = kv.(string)
continue
}

newAttr.Value = slog.AnyValue(kv)
c.fields = append(c.fields, newAttr)
}
}
}

func (c *capturingLogger) Debug(msg string, keyvals ...any) {
c.capture(msg, keyvals...)
}

func (c *capturingLogger) Info(msg string, keyvals ...any) {
c.capture(msg, keyvals...)
}

func (c *capturingLogger) Warn(msg string, keyvals ...any) {
c.capture(msg, keyvals...)
}

func (c *capturingLogger) Error(msg string, keyvals ...any) {
c.capture(msg, keyvals...)
}

func (c *capturingLogger) With(args ...any) component.Logger {
c.capture("", args...)
return c
}

func ptr[T any](v T) *T {
return &v
}

func TestJsonLogger_Report(t *testing.T) {
var (
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
now = time.Now().Unix()
logger = &capturingLogger{}

vulns = []*ocsf.VulnerabilityFinding{
{
ActivityId: ocsf.VulnerabilityFinding_ACTIVITY_ID_CREATE,
CategoryUid: ocsf.VulnerabilityFinding_CATEGORY_UID_FINDINGS,
ClassUid: ocsf.VulnerabilityFinding_CLASS_UID_VULNERABILITY_FINDING,
Confidence: ptr("MEDIUM"),
ConfidenceId: ptr(ocsf.VulnerabilityFinding_CONFIDENCE_ID_LOW),
Count: ptr(int32(1)),
FindingInfo: &ocsf.FindingInfo{
CreatedTime: &now,
DataSources: []string{
"/main.go",
},
Desc: ptr("lots of hacks"),
FirstSeenTime: &now,
LastSeenTime: &now,
ModifiedTime: &now,
ProductUid: ptr("gosec"),
Title: "You have lots of issues",
Uid: "1",
},
Message: ptr("lots of hacks"),
Resource: &ocsf.ResourceDetails{
Uid: ptr(
strings.Join([]string{
"/main.go",
"1",
"1",
},
":",
),
),
Data: &structpb.Value{
Kind: &structpb.Value_StringValue{
StringValue: "1",
},
},
},
RawData: ptr(`{"issues" : []}`),
Severity: ptr("CRITICAL"),
SeverityId: ocsf.VulnerabilityFinding_SEVERITY_ID_CRITICAL,
StartTime: &now,
Status: ptr("opened"),
Time: now,
TypeUid: int64(
ocsf.VulnerabilityFinding_CLASS_UID_VULNERABILITY_FINDING.Number()*
100 +
ocsf.VulnerabilityFinding_ACTIVITY_ID_CREATE.Number(),
),
Vulnerabilities: []*ocsf.Vulnerability{
{
Cwe: &ocsf.Cwe{
Uid: "1",
SrcUrl: ptr("https://issues.com/1"),
},
},
},
},
{
ActivityId: ocsf.VulnerabilityFinding_ACTIVITY_ID_CREATE,
CategoryUid: ocsf.VulnerabilityFinding_CATEGORY_UID_FINDINGS,
ClassUid: ocsf.VulnerabilityFinding_CLASS_UID_VULNERABILITY_FINDING,
Confidence: ptr("HIGH"),
ConfidenceId: ptr(ocsf.VulnerabilityFinding_CONFIDENCE_ID_HIGH),
Count: ptr(int32(2)),
FindingInfo: &ocsf.FindingInfo{
CreatedTime: &now,
DataSources: []string{
"/internal/sketchy/sketch.go",
},
Desc: ptr("stop writing hacky code"),
FirstSeenTime: &now,
LastSeenTime: &now,
ModifiedTime: &now,
ProductUid: ptr("gosec"),
Title: "You have lots of hacky code",
Uid: "2",
},
Message: ptr("lots of hacky code"),
Resource: &ocsf.ResourceDetails{
Uid: ptr(
strings.Join([]string{
"/internal/sketchy/sketch.go",
"10",
"1",
},
":",
),
),
Data: &structpb.Value{
Kind: &structpb.Value_StringValue{
StringValue: "2",
},
},
},
RawData: ptr(`{"issues" : [{"id": 2}]}`),
Severity: ptr("HIGH"),
SeverityId: ocsf.VulnerabilityFinding_SEVERITY_ID_HIGH,
StartTime: &now,
Status: ptr("opened"),
Time: now,
TypeUid: int64(
ocsf.VulnerabilityFinding_CLASS_UID_VULNERABILITY_FINDING.Number()*
100 +
ocsf.VulnerabilityFinding_ACTIVITY_ID_CREATE.Number(),
),
Vulnerabilities: []*ocsf.Vulnerability{
{
Cwe: &ocsf.Cwe{
Uid: "2",
SrcUrl: ptr("https://issues.com/2"),
},
},
},
},
{
ActivityId: ocsf.VulnerabilityFinding_ACTIVITY_ID_CREATE,
CategoryUid: ocsf.VulnerabilityFinding_CATEGORY_UID_FINDINGS,
ClassUid: ocsf.VulnerabilityFinding_CLASS_UID_VULNERABILITY_FINDING,
Confidence: ptr("LOW"),
ConfidenceId: ptr(ocsf.VulnerabilityFinding_CONFIDENCE_ID_LOW),
Count: ptr(int32(3)),
FindingInfo: &ocsf.FindingInfo{
CreatedTime: &now,
DataSources: []string{
"/internal/sketchy/hacks.go",
},
Desc: ptr("stop writing hacks"),
FirstSeenTime: &now,
LastSeenTime: &now,
ModifiedTime: &now,
ProductUid: ptr("gosec"),
Title: "You have lots of hacks",
Uid: "3",
},
Message: ptr("lots of hacks"),
Resource: &ocsf.ResourceDetails{
Uid: ptr(
strings.Join([]string{
"/internal/sketchy/hacks.go",
"123",
"13",
},
":",
),
),
Data: &structpb.Value{
Kind: &structpb.Value_StringValue{
StringValue: "3",
},
},
},
RawData: ptr(`{"issues" : [{"id": 3}]}`),
Severity: ptr("HIGH"),
SeverityId: ocsf.VulnerabilityFinding_SEVERITY_ID_HIGH,
StartTime: &now,
Status: ptr("opened"),
Time: now,
TypeUid: int64(
ocsf.VulnerabilityFinding_CLASS_UID_VULNERABILITY_FINDING.Number()*
100 +
ocsf.VulnerabilityFinding_ACTIVITY_ID_CREATE.Number(),
),
Vulnerabilities: []*ocsf.Vulnerability{
{
Cwe: &ocsf.Cwe{
Uid: "3",
SrcUrl: ptr("https://issues.com/3"),
},
},
},
},
}
st = &store{
vulns: vulns,
}
)

defer cancel()

t.Run("it logs correctly 3 findings", func(t *testing.T) {
require.NoError(
t,
Main(
ctx,
component.RunnerWithLogger(logger),
component.RunnerWithStorer("local", st),
component.RunnerWithInstanceID("3d3168e9-4ae7-4431-917c-438715b3e561"),
),
)
require.NotEmpty(t, logger.logs)
var numFindings int
for _, l := range logger.logs {
if l == "found finding" {
numFindings++
}
}
assert.Equal(t, 3, numFindings)

var findings []*ocsf.VulnerabilityFinding
for _, l := range logger.fields {
if l.Key == "finding" {
var finding ocsf.VulnerabilityFinding
b := []byte(l.Value.String())
require.NoError(t, protojson.Unmarshal(b, &finding))
findings = append(findings, &finding)
}
}

assert.Subset(t, vulns, findings)
})
}
10 changes: 10 additions & 0 deletions new-components/reporters/json-logger/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
reporter:
build:
context: ../..
args:
- COMPONENT_PATH=reporters/json-logger
- COMPONENT_BINARY_SOURCE_PATH=cmd/main.go
platform: linux/amd64
env_file:
- .env
Loading

0 comments on commit bd1db14

Please sign in to comment.