Skip to content

Commit

Permalink
Add evidence to modules table
Browse files Browse the repository at this point in the history
  • Loading branch information
EyalDelarea committed Dec 31, 2024
1 parent f8914ce commit e88040f
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 13 deletions.
55 changes: 48 additions & 7 deletions artifactory/utils/commandsummary/buildinfosummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,17 @@ func (bis *BuildInfoSummary) generateModulesMarkdown(modules ...buildInfo.Module
if len(subModules) == 0 {
continue
}
if !scannableModuleType[subModules[0].Type] {
// Check if the artifacts inside the module contains evidences
evidenceExists := checkEvidence(subModules)

if !scannableModuleType[subModules[0].Type] && !evidenceExists {
tree, err := bis.generateModuleArtifactTree(rootModuleID, subModules)
if err != nil {
return "", err
}
modulesMarkdown.WriteString(tree)
} else {
view, err := bis.generateModuleTableView(rootModuleID, subModules)
view, err := bis.generateModuleTableView(rootModuleID, subModules, evidenceExists)
if err != nil {
return "", err
}
Expand All @@ -146,6 +149,19 @@ func (bis *BuildInfoSummary) generateModulesMarkdown(modules ...buildInfo.Module
return modulesMarkdown.String(), nil
}

func checkEvidence(modules []buildInfo.Module) bool {
// TODO this has to be changed to SHA, as name can repeat
for _, module := range modules {
for _, artifact := range module.Artifacts {
_, exists := StaticMarkdownConfig.artifactsEvidencesMapping[artifact.Name]
if exists {
return true
}
}
}
return false
}

func (bis *BuildInfoSummary) generateModuleArtifactTree(rootModuleID string, nestedModules []buildInfo.Module) (string, error) {
if len(nestedModules) == 0 {
return "", nil
Expand All @@ -170,17 +186,17 @@ func (bis *BuildInfoSummary) generateModuleArtifactTree(rootModuleID string, nes
return markdownBuilder.String(), nil
}

func (bis *BuildInfoSummary) generateModuleTableView(rootModuleID string, subModules []buildInfo.Module) (string, error) {
func (bis *BuildInfoSummary) generateModuleTableView(rootModuleID string, subModules []buildInfo.Module, evidenceExists bool) (string, error) {
var markdownBuilder strings.Builder
markdownBuilder.WriteString(generateModuleHeader(rootModuleID))
markdownBuilder.WriteString(generateModuleTableHeader())
markdownBuilder.WriteString(generateModuleTableHeader(evidenceExists))
isMultiModule := len(subModules) > 1
nestedModuleMarkdownTree, err := bis.generateTableModuleMarkdown(subModules, rootModuleID, isMultiModule)
if err != nil {
return "", err
}
scanResult := getScanResults(extractDockerImageTag(subModules))
markdownBuilder.WriteString(generateTableRow(nestedModuleMarkdownTree, scanResult))
markdownBuilder.WriteString(generateTableRow(nestedModuleMarkdownTree, scanResult, evidenceExists))
return markdownBuilder.String(), nil
}

Expand Down Expand Up @@ -351,14 +367,39 @@ func generateModuleHeader(parentModuleID string) string {
return fmt.Sprintf("\n\n**%s**\n\n", parentModuleID)
}

func generateModuleTableHeader() string {
func generateModuleTableHeader(evidenceExists bool) string {
if evidenceExists {
return "\n\n| Artifacts | Evidence created | Security Violations | Security Issues |\n|:------------|:---------------------|:------------------|:------------------|\n"
}
return "\n\n| Artifacts | Security Violations | Security Issues |\n|:------------|:---------------------|:------------------|\n"
}

func generateTableRow(nestedModuleMarkdownTree string, scanResult ScanResult) string {
func generateTableRow(nestedModuleMarkdownTree string, scanResult ScanResult, evidenceExists bool) string {
if evidenceExists {
return fmt.Sprintf(" %s | %s | %s | %s |\n", fitInsideMarkdownTable(nestedModuleMarkdownTree), getEvidenceLinkFromModule(nestedModuleMarkdownTree), appendSpacesToTableColumn(scanResult.GetViolations()), appendSpacesToTableColumn(scanResult.GetVulnerabilities()))
}
return fmt.Sprintf(" %s | %s | %s |\n", fitInsideMarkdownTable(nestedModuleMarkdownTree), appendSpacesToTableColumn(scanResult.GetViolations()), appendSpacesToTableColumn(scanResult.GetVulnerabilities()))
}

func getEvidenceLinkFromModule(moduleTree string) string {
for _, artifact := range StaticMarkdownConfig.artifactsEvidencesMapping {
if strings.Contains(moduleTree, artifact.Name) {
evidenceUrl, err := GenerateArtifactEvidenceUrl(path.Join(artifact.OriginalDeploymentRepo, artifact.Path))
if err != nil {
log.Warn(err)
}
if StaticMarkdownConfig.IsExtendedSummary() {
return fmt.Sprintf("[Build-signature](%s)", evidenceUrl)
}
// TODO add evidence support link
return fmt.Sprintf("πŸ”Ž [Enable evidence support](%s)", "somelink")
}
}
// TODO handle no evidence
return fmt.Sprintf("[Learn more about evidence](%s)", "someLink")

}

func fitInsideMarkdownTable(str string) string {
return strings.ReplaceAll(str, "\n", "<br>")
}
Expand Down
85 changes: 79 additions & 6 deletions artifactory/utils/commandsummary/buildinfosummary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import (
)

const (
buildInfoTable = "build-info-table.md"
dockerImageModule = "docker-image-module.md"
genericModule = "generic-module.md"
mavenModule = "maven-module.md"
mavenNestedModule = "maven-nested-module.md"
dockerMultiArchModule = "multiarch-docker-image.md"
buildInfoTable = "build-info-table.md"
dockerImageModule = "docker-image-module.md"
genericModule = "generic-module.md"
mavenModule = "maven-module.md"
mavenNestedModule = "maven-nested-module.md"
dockerMultiArchModule = "multiarch-docker-image.md"
dockerMultiArchModuleEvidence = "multiarch-docker-image-evidence.md"
)

type MockScanResult struct {
Expand Down Expand Up @@ -500,6 +501,78 @@ func TestGroupModules(t *testing.T) {
}
}

func TestModuleWithEvidence(t *testing.T) {
buildInfoSummary, cleanUp := prepareBuildInfoTest()
defer func() {
cleanUp()
}()
StaticMarkdownConfig.artifactsEvidencesMapping = make(map[string]buildinfo.Artifact)
StaticMarkdownConfig.artifactsEvidencesMapping["list.manifest.json"] = buildinfo.Artifact{
Path: "multiarch-image/sha256",
Name: "sha256",
}
var builds = []*buildinfo.BuildInfo{
{
Name: "dockerx",
Number: "1",
Started: "2024-08-12T11:11:50.198+0300",
Modules: []buildinfo.Module{
{
Properties: map[string]interface{}{
"docker.image.tag": "ecosysjfrog.jfrog.io/docker-local/multiarch-image:1",
},
Type: "docker",
Id: "multiarch-image:1",
Artifacts: []buildinfo.Artifact{
{
Type: "json",
Checksum: buildinfo.Checksum{
Sha1: "fa",
Sha256: "2217",
Md5: "ba0",
},
Name: "list.manifest.json",
Path: "multiarch-image/1/list.manifest.json",
OriginalDeploymentRepo: "docker-local",
},
},
},
{
Type: "docker",
Parent: "multiarch-image:1",
Id: "linux/amd64/multiarch-image:1",
Artifacts: []buildinfo.Artifact{
{
Checksum: buildinfo.Checksum{
Sha1: "32",
Sha256: "sha256:552c",
Md5: "f56",
},
Name: "manifest.json",
Path: "multiarch-image/sha256",
OriginalDeploymentRepo: "docker-local",
},
},
},
},
},
}

t.Run("Extended Summary", func(t *testing.T) {
StaticMarkdownConfig.setExtendedSummary(true)
res, err := buildInfoSummary.buildInfoModules(builds)
assert.NoError(t, err)
testMarkdownOutput(t, getTestDataFile(t, dockerMultiArchModuleEvidence), res)
})
t.Run("Basic Summary", func(t *testing.T) {
StaticMarkdownConfig.setExtendedSummary(false)
res, err := buildInfoSummary.buildInfoModules(builds)
assert.NoError(t, err)
testMarkdownOutput(t, getTestDataFile(t, dockerMultiArchModuleEvidence), res)
})

}

// Tests data files are location artifactory/commands/testdata/command_summary
func getTestDataFile(t *testing.T, fileName string) string {
var modulesPath string
Expand Down
6 changes: 6 additions & 0 deletions artifactory/utils/commandsummary/markdownConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commandsummary
import (
"encoding/json"
"fmt"
buildInfo "github.com/jfrog/build-info-go/entities"
"net/http"
"net/url"
"strings"
Expand All @@ -23,6 +24,8 @@ type MarkdownConfig struct {
platformMajorVersion int
// Static mapping of scan results to be used in the summary
scanResultsMapping map[string]ScanResult
// Static mapping of artifacts evidences
artifactsEvidencesMapping map[string]buildInfo.Artifact
}

const extendedSummaryLandPage = "https://jfrog.com/help/access?xinfo:appid=csh-gen-gitbook"
Expand Down Expand Up @@ -60,6 +63,9 @@ func (mg *MarkdownConfig) GetExtendedSummaryLangPage() string {
func (mg *MarkdownConfig) SetScanResultsMapping(resultsMap map[string]ScanResult) {
mg.scanResultsMapping = resultsMap
}
func (mg *MarkdownConfig) SetArtifactsEvidencesMapping(artifactsEvidencesMapping map[string]buildInfo.Artifact) {
mg.artifactsEvidencesMapping = artifactsEvidencesMapping
}

// Initializes the command summary values that effect Markdown generation
func InitMarkdownGenerationValues(serverUrl string, platformMajorVersion int) (err error) {
Expand Down
12 changes: 12 additions & 0 deletions artifactory/utils/commandsummary/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

const (
artifactory7UiFormat = "%sui/repos/tree/General/%s?clearFilter=true"
artifactory7UiEvidenceFormat = "%sui/repos/tree/Evidence/%s?clearFilter=true"
artifactory6UiFormat = "%sartifactory/webapp/#/artifacts/browse/tree/General/%s"
artifactoryDockerPackagesUiFormat = "%s/ui/packages/docker:%s/sha256__%s"
githubWorkflowEnv = "GITHUB_WORKFLOW"
Expand All @@ -27,6 +28,17 @@ func GenerateArtifactUrl(pathInRt string, section summarySection) (url string, e
return
}

func GenerateArtifactEvidenceUrl(pathInRt string) (url string, err error) {
if StaticMarkdownConfig.GetPlatformMajorVersion() == 6 {
// todo handle not supported
url = fmt.Sprintf(artifactory6UiFormat, StaticMarkdownConfig.GetPlatformUrl(), pathInRt)
} else {
url = fmt.Sprintf(artifactory7UiEvidenceFormat, StaticMarkdownConfig.GetPlatformUrl(), pathInRt)
}
url, err = addGitHubTrackingToUrl(url, artifactsSection)
return
}

func WrapCollapsableMarkdown(title, markdown string, headerSize int) string {
return fmt.Sprintf("\n\n\n<details open>\n\n<summary> <h%d> %s </h%d></summary><p></p>\n\n%s\n\n</details>\n\n\n", headerSize, title, headerSize, markdown)
}
Expand Down
5 changes: 5 additions & 0 deletions artifactory/utils/commandsummary/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ const (
)

func TestGenerateArtifactUrl(t *testing.T) {
// Used to
_, cleanUp := prepareBuildInfoTest()
defer func() {
cleanUp()
}()
cases := []struct {
testName string
projectKey string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@


<h3>Published Modules</h3>



**multiarch-image:1**



| Artifacts | Evidence created | Security Violations | Security Issues |
| :------------ | :--------------------- | :------------------ | :------------------ |
| <a href="https://jfrog.com/help/access?xinfo:appid=csh-gen-gitbook">🐸 Enable the linkage to Artifactory</a><br><br><pre><details><summary>linux/amd64/multiarch-image:1</summary><br>πŸ“¦ docker-local<br>└── πŸ“ multiarch-image<br> └── πŸ“„ sha256<br><br></details></pre> | πŸ”Ž [Enable evidence support](somelink) | Not scanned | Not scanned |
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@


<h3>Published Modules</h3>



**multiarch-image:1**



| Artifacts | Evidence created | Security Violations | Security Issues |
| :------------ | :--------------------- | :------------------ | :------------------ |
| <pre><details><summary>linux/amd64/multiarch-image:1 <a href=https://myplatform.com/ui/packages/docker:%2F%2Fmultiarch-image/sha256__sha256:552c>(🐸 View)</a></summary><br>πŸ“¦ docker-local<br>└── πŸ“ multiarch-image<br> └── <a href='https://myplatform.com/ui/repos/tree/General/docker-local/multiarch-image/sha256?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=packages' target="_blank">sha256</a><br><br></details></pre> | [Build-signature](https://myplatform.com/ui/repos/tree/Evidence/multiarch-image/sha256?clearFilter=true&gh_job_id=JFrog+CLI+Core+Tests&gh_section=artifacts) | Not scanned | Not scanned |

0 comments on commit e88040f

Please sign in to comment.