Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,5 @@ Code reviews are required for all submissions via GitHub pull requests.
- For each file you modify, update the license header. If it says 2024, change it to 2024-2025. If there's no license header, create one with the current year.
- if you add any new dependency to a constructor, remember to run wire ./...
- when creating PR message, keep it high-level, what functionality was added, don't add info about testing, no icons, no info about how the message was generated.
- app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts is a special case that we don't want to upgrade, so if it upgrades, put it back to main
- app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts is a special case that we don't want to upgrade, so if it upgrades, put it back to main
- when creating a commit or PR message, NEVER add co-authored by or generated by Claude code
45 changes: 33 additions & 12 deletions pkg/attestation/crafter/materials/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,48 @@ func NewGitlabCrafter(schema *schemaapi.CraftingSchema_Material, backend *cascli
}

func (i *GitlabCrafter) Craft(ctx context.Context, filePath string) (*api.Attestation_Material, error) {
if err := i.validate(filePath); err != nil {
return nil, err
}

return uploadAndCraft(ctx, i.input, i.backend, filePath, i.logger)
}

func (i *GitlabCrafter) validate(filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("can't open the file: %w", err)
return nil, fmt.Errorf("can't open the file: %w", err)
}

var glReport report.Report
if err = json.Unmarshal(data, &glReport); err != nil {
return fmt.Errorf("error unmarshalling report file: %w", err)
return nil, fmt.Errorf("error unmarshalling report file: %w", err)
}

if !slices.Contains(supportedTypes, string(glReport.Scan.Type)) {
return fmt.Errorf("error loading Gitlab report. Missing scan type")
return nil, fmt.Errorf("error loading Gitlab report. Missing scan type")
}

m, err := uploadAndCraft(ctx, i.input, i.backend, filePath, i.logger)
if err != nil {
return nil, err
}

i.injectAnnotations(m, &glReport)

return m, nil
}

func (i *GitlabCrafter) injectAnnotations(m *api.Attestation_Material, glReport *report.Report) {
// Prefer scanner (the actual security tool) over analyzer (the wrapper/integration layer)
toolName := glReport.Scan.Scanner.Name
toolVersion := glReport.Scan.Scanner.Version

// Fallback to analyzer if scanner information is not available
if toolName == "" {
toolName = glReport.Scan.Analyzer.Name
toolVersion = glReport.Scan.Analyzer.Version
}

return nil
if toolName != "" {
if m.Annotations == nil {
m.Annotations = make(map[string]string)
}
m.Annotations[AnnotationToolNameKey] = toolName
if toolVersion != "" {
m.Annotations[AnnotationToolVersionKey] = toolVersion
}
}
}
33 changes: 30 additions & 3 deletions pkg/attestation/crafter/materials/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ func TestNewGitlabCrafter(t *testing.T) {

func TestGitlabCrafter_Craft(t *testing.T) {
testCases := []struct {
name string
filePath string
wantErr string
name string
filePath string
wantErr string
annotations map[string]string
}{
{
name: "invalid path",
Expand All @@ -86,14 +87,34 @@ func TestGitlabCrafter_Craft(t *testing.T) {
{
name: "sast report",
filePath: "./testdata/gl-sast-report.json",
annotations: map[string]string{
"chainloop.material.tool.name": "Semgrep",
"chainloop.material.tool.version": ":SKIP:",
},
},
{
name: "container scanning report",
filePath: "./testdata/gl-container-scanning-report.json",
annotations: map[string]string{
"chainloop.material.tool.name": "Trivy",
"chainloop.material.tool.version": "0.19.2",
},
},
{
name: "secret detection report",
filePath: "./testdata/gl-secret-detection-report.json",
annotations: map[string]string{
"chainloop.material.tool.name": "Gitleaks",
"chainloop.material.tool.version": ":SKIP:",
},
},
{
name: "sonarqube report",
filePath: "./testdata/gl-sonarqube-report.json",
annotations: map[string]string{
"chainloop.material.tool.name": "Sonar",
"chainloop.material.tool.version": "11.2.0.2797",
},
},
}

Expand Down Expand Up @@ -125,6 +146,12 @@ func TestGitlabCrafter_Craft(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, contractAPI.CraftingSchema_Material_GITLAB_SECURITY_REPORT.String(), got.MaterialType.String())
assert.True(t, got.UploadedToCas)

if tc.annotations != nil {
for k, v := range tc.annotations {
assert.Equal(t, v, got.Annotations[k])
}
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"version": "15.0.0",
"scan": {
"analyzer": {
"id": "SONARQUBE 2025.1.1.104738",
"name": "SONARQUBE - Enterprise",
"vendor": {
"name": "Sonar"
},
"version": "2025.1.1.104738"
},
"scanner": {
"id": "sonar_scan",
"name": "Sonar",
"vendor": {
"name": "Sonar"
},
"version": "11.2.0.2797"
},
"start_time": "2025-10-20T20:18:45",
"end_time": "2025-10-20T20:18:45",
"status": "success",
"messages": [],
"type": "sast"
},
"vulnerabilities": []
}
Loading