add gitleak to cli

This commit is contained in:
Maidul Islam
2023-05-15 19:31:36 -04:00
parent 781e0b24c8
commit b3e68cf3fb
47 changed files with 8065 additions and 128 deletions

25
cli/report/constants.go Normal file
View File

@@ -0,0 +1,25 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
const version = "v8.0.0"
const driver = "gitleaks"

81
cli/report/csv.go Normal file
View File

@@ -0,0 +1,81 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"encoding/csv"
"io"
"strconv"
)
// writeCsv writes the list of findings to a writeCloser.
func writeCsv(f []Finding, w io.WriteCloser) error {
if len(f) == 0 {
return nil
}
defer w.Close()
cw := csv.NewWriter(w)
err := cw.Write([]string{"RuleID",
"Commit",
"File",
"SymlinkFile",
"Secret",
"Match",
"StartLine",
"EndLine",
"StartColumn",
"EndColumn",
"Author",
"Message",
"Date",
"Email",
"Fingerprint",
})
if err != nil {
return err
}
for _, f := range f {
err = cw.Write([]string{f.RuleID,
f.Commit,
f.File,
f.SymlinkFile,
f.Secret,
f.Match,
strconv.Itoa(f.StartLine),
strconv.Itoa(f.EndLine),
strconv.Itoa(f.StartColumn),
strconv.Itoa(f.EndColumn),
f.Author,
f.Message,
f.Date,
f.Email,
f.Fingerprint,
})
if err != nil {
return err
}
}
cw.Flush()
return cw.Error()
}

108
cli/report/csv_test.go Normal file
View File

@@ -0,0 +1,108 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestWriteCSV(t *testing.T) {
tests := []struct {
findings []Finding
testReportName string
expected string
wantEmpty bool
}{
{
testReportName: "simple",
expected: filepath.Join(expectPath, "report", "csv_simple.csv"),
findings: []Finding{
{
RuleID: "test-rule",
Match: "line containing secret",
Secret: "a secret",
StartLine: 1,
EndLine: 2,
StartColumn: 1,
EndColumn: 2,
Message: "opps",
File: "auth.py",
SymlinkFile: "",
Commit: "0000000000000000",
Author: "John Doe",
Email: "johndoe@gmail.com",
Date: "10-19-2003",
Fingerprint: "fingerprint",
},
}},
{
wantEmpty: true,
testReportName: "empty",
expected: filepath.Join(expectPath, "report", "this_should_not_exist.csv"),
findings: []Finding{}},
}
for _, test := range tests {
tmpfile, err := os.Create(filepath.Join(tmpPath, test.testReportName+".csv"))
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
err = writeCsv(test.findings, tmpfile)
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
got, err := os.ReadFile(tmpfile.Name())
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
if test.wantEmpty {
if len(got) > 0 {
t.Errorf("Expected empty file, got %s", got)
}
os.Remove(tmpfile.Name())
continue
}
want, err := os.ReadFile(test.expected)
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
if string(got) != string(want) {
err = os.WriteFile(strings.Replace(test.expected, ".csv", ".got.csv", 1), got, 0644)
if err != nil {
t.Error(err)
}
t.Errorf("got %s, want %s", string(got), string(want))
}
os.Remove(tmpfile.Name())
}
}

72
cli/report/finding.go Normal file
View File

@@ -0,0 +1,72 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"strings"
)
// Finding contains information about strings that
// have been captured by a tree-sitter query.
type Finding struct {
Description string
StartLine int
EndLine int
StartColumn int
EndColumn int
Line string `json:"-"`
Match string
// Secret contains the full content of what is matched in
// the tree-sitter query.
Secret string
// File is the name of the file containing the finding
File string
SymlinkFile string
Commit string
// Entropy is the shannon entropy of Value
Entropy float32
Author string
Email string
Date string
Message string
Tags []string
// Rule is the name of the rule that was matched
RuleID string
// unique identifer
Fingerprint string
}
// Redact removes sensitive information from a finding.
func (f *Finding) Redact() {
f.Line = strings.Replace(f.Line, f.Secret, "REDACTED", -1)
f.Match = strings.Replace(f.Match, f.Secret, "REDACTED", -1)
f.Secret = "REDACTED"
}

View File

@@ -0,0 +1,48 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import "testing"
func TestRedact(t *testing.T) {
tests := []struct {
findings []Finding
redact bool
}{
{
redact: true,
findings: []Finding{
{
Secret: "line containing secret",
Match: "secret",
},
}},
}
for _, test := range tests {
for _, f := range test.findings {
f.Redact()
if f.Secret != "REDACTED" {
t.Error("redact not redacting: ", f.Secret)
}
}
}
}

37
cli/report/json.go Normal file
View File

@@ -0,0 +1,37 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"encoding/json"
"io"
)
func writeJson(findings []Finding, w io.WriteCloser) error {
if len(findings) == 0 {
findings = []Finding{}
}
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return encoder.Encode(findings)
}

111
cli/report/json_test.go Normal file
View File

@@ -0,0 +1,111 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestWriteJSON(t *testing.T) {
tests := []struct {
findings []Finding
testReportName string
expected string
wantEmpty bool
}{
{
testReportName: "simple",
expected: filepath.Join(expectPath, "report", "json_simple.json"),
findings: []Finding{
{
Description: "",
RuleID: "test-rule",
Match: "line containing secret",
Secret: "a secret",
StartLine: 1,
EndLine: 2,
StartColumn: 1,
EndColumn: 2,
Message: "opps",
File: "auth.py",
SymlinkFile: "",
Commit: "0000000000000000",
Author: "John Doe",
Email: "johndoe@gmail.com",
Date: "10-19-2003",
Tags: []string{},
},
}},
{
testReportName: "empty",
expected: filepath.Join(expectPath, "report", "empty.json"),
findings: []Finding{}},
}
for _, test := range tests {
// create tmp file using os.TempDir()
tmpfile, err := os.Create(filepath.Join(tmpPath, test.testReportName+".json"))
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
err = writeJson(test.findings, tmpfile)
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
got, err := os.ReadFile(tmpfile.Name())
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
if test.wantEmpty {
if len(got) > 0 {
os.Remove(tmpfile.Name())
t.Errorf("Expected empty file, got %s", got)
}
os.Remove(tmpfile.Name())
continue
}
want, err := os.ReadFile(test.expected)
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
if string(got) != string(want) {
err = os.WriteFile(strings.Replace(test.expected, ".json", ".got.json", 1), got, 0644)
if err != nil {
t.Error(err)
}
t.Errorf("got %s, want %s", string(got), string(want))
}
os.Remove(tmpfile.Name())
}
}

54
cli/report/report.go Normal file
View File

@@ -0,0 +1,54 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"os"
"strings"
"github.com/Infisical/infisical-merge/config"
)
const (
// https://cwe.mitre.org/data/definitions/798.html
CWE = "CWE-798"
CWE_DESCRIPTION = "Use of Hard-coded Credentials"
)
func Write(findings []Finding, cfg config.Config, ext string, reportPath string) error {
file, err := os.Create(reportPath)
if err != nil {
return err
}
ext = strings.ToLower(ext)
switch ext {
case ".json", "json":
err = writeJson(findings, file)
case ".csv", "csv":
err = writeCsv(findings, file)
case ".sarif", "sarif":
err = writeSarif(cfg, findings, file)
}
return err
}

133
cli/report/report_test.go Normal file
View File

@@ -0,0 +1,133 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"os"
"path/filepath"
"strconv"
"testing"
"github.com/Infisical/infisical-merge/config"
)
const (
expectPath = "../testdata/expected/"
tmpPath = "../testdata/tmp"
)
func TestReport(t *testing.T) {
tests := []struct {
findings []Finding
ext string
wantEmpty bool
}{
{
ext: "json",
findings: []Finding{
{
RuleID: "test-rule",
},
},
},
{
ext: ".json",
findings: []Finding{
{
RuleID: "test-rule",
},
},
},
{
ext: ".jsonj",
findings: []Finding{
{
RuleID: "test-rule",
},
},
wantEmpty: true,
},
{
ext: ".csv",
findings: []Finding{
{
RuleID: "test-rule",
},
},
},
{
ext: "csv",
findings: []Finding{
{
RuleID: "test-rule",
},
},
},
{
ext: "CSV",
findings: []Finding{
{
RuleID: "test-rule",
},
},
},
// {
// ext: "SARIF",
// findings: []Finding{
// {
// RuleID: "test-rule",
// },
// },
// },
}
for i, test := range tests {
tmpfile, err := os.Create(filepath.Join(tmpPath, strconv.Itoa(i)+test.ext))
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
err = Write(test.findings, config.Config{}, test.ext, tmpfile.Name())
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
got, err := os.ReadFile(tmpfile.Name())
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
os.Remove(tmpfile.Name())
if len(got) == 0 && !test.wantEmpty {
t.Errorf("got empty file with extension " + test.ext)
}
if test.wantEmpty {
if len(got) > 0 {
t.Errorf("Expected empty file, got %s", got)
}
continue
}
}
}

237
cli/report/sarif.go Normal file
View File

@@ -0,0 +1,237 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"encoding/json"
"fmt"
"io"
"github.com/Infisical/infisical-merge/config"
)
func writeSarif(cfg config.Config, findings []Finding, w io.WriteCloser) error {
sarif := Sarif{
Schema: "https://json.schemastore.org/sarif-2.1.0.json",
Version: "2.1.0",
Runs: getRuns(cfg, findings),
}
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return encoder.Encode(sarif)
}
func getRuns(cfg config.Config, findings []Finding) []Runs {
return []Runs{
{
Tool: getTool(cfg),
Results: getResults(findings),
},
}
}
func getTool(cfg config.Config) Tool {
tool := Tool{
Driver: Driver{
Name: driver,
SemanticVersion: version,
InformationUri: "https://github.com/Infisical/infisical",
Rules: getRules(cfg),
},
}
// if this tool has no rules, ensure that it is represented as [] instead of null/nil
if hasEmptyRules(tool) {
tool.Driver.Rules = make([]Rules, 0)
}
return tool
}
func hasEmptyRules(tool Tool) bool {
return len(tool.Driver.Rules) == 0
}
func getRules(cfg config.Config) []Rules {
// TODO for _, rule := range cfg.Rules {
var rules []Rules
for _, rule := range cfg.OrderedRules() {
shortDescription := ShortDescription{
Text: rule.Description,
}
if rule.Regex != nil {
shortDescription = ShortDescription{
Text: rule.Regex.String(),
}
} else if rule.Path != nil {
shortDescription = ShortDescription{
Text: rule.Path.String(),
}
}
rules = append(rules, Rules{
ID: rule.RuleID,
Name: rule.Description,
Description: shortDescription,
})
}
return rules
}
func messageText(f Finding) string {
if f.Commit == "" {
return fmt.Sprintf("%s has detected secret for file %s.", f.RuleID, f.File)
}
return fmt.Sprintf("%s has detected secret for file %s at commit %s.", f.RuleID, f.File, f.Commit)
}
func getResults(findings []Finding) []Results {
results := []Results{}
for _, f := range findings {
r := Results{
Message: Message{
Text: messageText(f),
},
RuleId: f.RuleID,
Locations: getLocation(f),
// This information goes in partial fingerprings until revision
// data can be added somewhere else
PartialFingerPrints: PartialFingerPrints{
CommitSha: f.Commit,
Email: f.Email,
CommitMessage: f.Message,
Date: f.Date,
Author: f.Author,
},
}
results = append(results, r)
}
return results
}
func getLocation(f Finding) []Locations {
uri := f.File
if f.SymlinkFile != "" {
uri = f.SymlinkFile
}
return []Locations{
{
PhysicalLocation: PhysicalLocation{
ArtifactLocation: ArtifactLocation{
URI: uri,
},
Region: Region{
StartLine: f.StartLine,
EndLine: f.EndLine,
StartColumn: f.StartColumn,
EndColumn: f.EndColumn,
Snippet: Snippet{
Text: f.Secret,
},
},
},
},
}
}
type PartialFingerPrints struct {
CommitSha string `json:"commitSha"`
Email string `json:"email"`
Author string `json:"author"`
Date string `json:"date"`
CommitMessage string `json:"commitMessage"`
}
type Sarif struct {
Schema string `json:"$schema"`
Version string `json:"version"`
Runs []Runs `json:"runs"`
}
type ShortDescription struct {
Text string `json:"text"`
}
type FullDescription struct {
Text string `json:"text"`
}
type Rules struct {
ID string `json:"id"`
Name string `json:"name"`
Description ShortDescription `json:"shortDescription"`
}
type Driver struct {
Name string `json:"name"`
SemanticVersion string `json:"semanticVersion"`
InformationUri string `json:"informationUri"`
Rules []Rules `json:"rules"`
}
type Tool struct {
Driver Driver `json:"driver"`
}
type Message struct {
Text string `json:"text"`
}
type ArtifactLocation struct {
URI string `json:"uri"`
}
type Region struct {
StartLine int `json:"startLine"`
StartColumn int `json:"startColumn"`
EndLine int `json:"endLine"`
EndColumn int `json:"endColumn"`
Snippet Snippet `json:"snippet"`
}
type Snippet struct {
Text string `json:"text"`
}
type PhysicalLocation struct {
ArtifactLocation ArtifactLocation `json:"artifactLocation"`
Region Region `json:"region"`
}
type Locations struct {
PhysicalLocation PhysicalLocation `json:"physicalLocation"`
}
type Results struct {
Message Message `json:"message"`
RuleId string `json:"ruleId"`
Locations []Locations `json:"locations"`
PartialFingerPrints `json:"partialFingerprints"`
}
type Runs struct {
Tool Tool `json:"tool"`
Results []Results `json:"results"`
}

133
cli/report/sarif_test.go Normal file
View File

@@ -0,0 +1,133 @@
// MIT License
// Copyright (c) 2019 Zachary Rice
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package report
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/Infisical/infisical-merge/config"
"github.com/spf13/viper"
)
const configPath = "../testdata/config/"
func TestWriteSarif(t *testing.T) {
tests := []struct {
findings []Finding
testReportName string
expected string
wantEmpty bool
cfgName string
}{
{
cfgName: "simple",
testReportName: "simple",
expected: filepath.Join(expectPath, "report", "sarif_simple.sarif"),
findings: []Finding{
{
Description: "A test rule",
RuleID: "test-rule",
Match: "line containing secret",
Secret: "a secret",
StartLine: 1,
EndLine: 2,
StartColumn: 1,
EndColumn: 2,
Message: "opps",
File: "auth.py",
Commit: "0000000000000000",
Author: "John Doe",
Email: "johndoe@gmail.com",
Date: "10-19-2003",
Tags: []string{},
},
}},
}
for _, test := range tests {
// create tmp file using os.TempDir()
tmpfile, err := os.Create(filepath.Join(tmpPath, test.testReportName+".json"))
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
viper.Reset()
viper.AddConfigPath(configPath)
viper.SetConfigName(test.cfgName)
viper.SetConfigType("toml")
err = viper.ReadInConfig()
if err != nil {
t.Error(err)
}
var vc config.ViperConfig
err = viper.Unmarshal(&vc)
if err != nil {
t.Error(err)
}
cfg, err := vc.Translate()
if err != nil {
t.Error(err)
}
err = writeSarif(cfg, test.findings, tmpfile)
fmt.Println(cfg)
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
got, err := os.ReadFile(tmpfile.Name())
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
if test.wantEmpty {
if len(got) > 0 {
os.Remove(tmpfile.Name())
t.Errorf("Expected empty file, got %s", got)
}
os.Remove(tmpfile.Name())
continue
}
want, err := os.ReadFile(test.expected)
if err != nil {
os.Remove(tmpfile.Name())
t.Error(err)
}
if string(got) != string(want) {
err = os.WriteFile(strings.Replace(test.expected, ".sarif", ".got.sarif", 1), got, 0644)
if err != nil {
t.Error(err)
}
t.Errorf("got %s, want %s", string(got), string(want))
}
os.Remove(tmpfile.Name())
}
}