diff --git a/checksumcalculator/checksumcalculator.go b/checksumcalculator/checksumcalculator.go index 512f84b5..e7f279c2 100644 --- a/checksumcalculator/checksumcalculator.go +++ b/checksumcalculator/checksumcalculator.go @@ -9,18 +9,24 @@ import ( yaml "gopkg.in/yaml.v2" ) -type ChecksumCalculator struct { +type ChecksumCalculator interface { + SuggestTalismanRC(fileNamePatterns []string) string + CalculateCollectiveChecksumForPattern(fileNamePattern string) string +} + +type DefaultChecksumCalculator struct { gitAdditions []gitrepo.Addition + hasher utility.SHA256Hasher } //NewChecksumCalculator returns new instance of the CheckSumDetector -func NewChecksumCalculator(gitAdditions []gitrepo.Addition) *ChecksumCalculator { - cc := ChecksumCalculator{gitAdditions: gitAdditions} +func NewChecksumCalculator(hasher utility.SHA256Hasher, gitAdditions []gitrepo.Addition) ChecksumCalculator { + cc := DefaultChecksumCalculator{hasher: hasher, gitAdditions: gitAdditions} return &cc } //SuggestTalismanRC returns the suggestion for .talismanrc format -func (cc *ChecksumCalculator) SuggestTalismanRC(fileNamePatterns []string) string { +func (cc *DefaultChecksumCalculator) SuggestTalismanRC(fileNamePatterns []string) string { var fileIgnoreConfigs []talismanrc.FileIgnoreConfig result := "" for _, pattern := range fileNamePatterns { @@ -39,7 +45,7 @@ func (cc *ChecksumCalculator) SuggestTalismanRC(fileNamePatterns []string) strin return result } -func (cc *ChecksumCalculator) CalculateCollectiveChecksumForPattern(fileNamePattern string) string { +func (cc *DefaultChecksumCalculator) CalculateCollectiveChecksumForPattern(fileNamePattern string) string { var patternPaths []string currentCollectiveChecksum := "" for _, addition := range cc.gitAdditions { @@ -50,7 +56,7 @@ func (cc *ChecksumCalculator) CalculateCollectiveChecksumForPattern(fileNamePatt // Calculate current collective checksum patternPaths = utility.UniqueItems(patternPaths) if len(patternPaths) != 0 { - currentCollectiveChecksum = utility.CollectiveSHA256Hash(patternPaths) + currentCollectiveChecksum = cc.hasher.CollectiveSHA256Hash(patternPaths) } return currentCollectiveChecksum } diff --git a/detector/base64_aggressive_detector_test.go b/detector/base64_aggressive_detector_test.go index 7326bb1e..d4c69f99 100644 --- a/detector/base64_aggressive_detector_test.go +++ b/detector/base64_aggressive_detector_test.go @@ -1,6 +1,7 @@ package detector import ( + "talisman/utility" "testing" "talisman/gitrepo" @@ -18,7 +19,7 @@ func TestShouldFlagPotentialAWSAccessKeysInAggressiveMode(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().AggressiveMode().Test(additions, talismanRC, results) + NewFileContentDetector().AggressiveMode().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.True(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -29,7 +30,7 @@ func TestShouldFlagPotentialAWSAccessKeysAtPropertyDefinitionInAggressiveMode(t filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().AggressiveMode().Test(additions, talismanRC, results) + NewFileContentDetector().AggressiveMode().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.True(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -40,7 +41,7 @@ func TestShouldNotFlagPotentialSecretsWithinSafeJavaCodeEvenInAggressiveMode(t * filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().AggressiveMode().Test(additions, talismanRC, results) + NewFileContentDetector().AggressiveMode().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) if results == nil { additions = nil } diff --git a/detector/checksum_compare.go b/detector/checksum_compare.go index 22123be0..1aebde3e 100644 --- a/detector/checksum_compare.go +++ b/detector/checksum_compare.go @@ -1,28 +1,29 @@ package detector import ( + "talisman/checksumcalculator" "talisman/gitrepo" "talisman/talismanrc" "talisman/utility" ) type ChecksumCompare struct { - additions []gitrepo.Addition - ignoreConfig *talismanrc.TalismanRC + calculator checksumcalculator.ChecksumCalculator + hasher utility.SHA256Hasher + talismanRC *talismanrc.TalismanRC } //NewChecksumCompare returns new instance of the ChecksumCompare -func NewChecksumCompare(gitAdditions []gitrepo.Addition, talismanRCConfig *talismanrc.TalismanRC) *ChecksumCompare { - cc := ChecksumCompare{additions: gitAdditions, ignoreConfig: talismanRCConfig} - return &cc +func NewChecksumCompare(calculator checksumcalculator.ChecksumCalculator, hasher utility.SHA256Hasher, talismanRCConfig *talismanrc.TalismanRC) ChecksumCompare { + return ChecksumCompare{calculator: calculator, hasher: hasher, talismanRC: talismanRCConfig} } func (cc *ChecksumCompare) IsScanNotRequired(addition gitrepo.Addition) bool { - currentCollectiveChecksum := utility.CollectiveSHA256Hash([]string{string(addition.Path)}) + currentCollectiveChecksum := cc.hasher.CollectiveSHA256Hash([]string{string(addition.Path)}) declaredCheckSum := "" - for _, ignore := range cc.ignoreConfig.FileIgnoreConfig { + for _, ignore := range cc.talismanRC.FileIgnoreConfig { if addition.Matches(ignore.FileName) { - currentCollectiveChecksum = utility.CollectiveSHA256Hash([]string{ignore.FileName}) + currentCollectiveChecksum = cc.calculator.CalculateCollectiveChecksumForPattern(ignore.FileName) declaredCheckSum = ignore.Checksum } } diff --git a/detector/checksum_compare_test.go b/detector/checksum_compare_test.go index da319532..8885c2ab 100644 --- a/detector/checksum_compare_test.go +++ b/detector/checksum_compare_test.go @@ -1,20 +1,52 @@ package detector import ( - "talisman/utility" - "testing" - + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "talisman/gitrepo" + mockchecksumcalculator "talisman/internal/mock/checksumcalculator" + mockutility "talisman/internal/mock/utility" + "talisman/talismanrc" + "testing" ) -func TestShouldReturnCorrectFileHash(t *testing.T) { - checksumSomeFile := utility.CollectiveSHA256Hash([]string{"some_file.pem"}) - checksumTestSomeFile := utility.CollectiveSHA256Hash([]string{"test/some_file.pem"}) - assert.Equal(t, checksumSomeFile, "87139cc4d975333b25b6275f97680604add51b84eb8f4a3b9dcbbc652e6f27ac", "Should be equal to some_file.pem hash value") - assert.Equal(t, checksumTestSomeFile, "25bd31a28bf9d4e06327f1c4a5cab2260574ae508803f66adcc393350e994866", "Should be equal to test/some_file.pem hash value") -} +func TestChecksumCompare_IsScanNotRequired(t *testing.T) { + + t.Run("should return false if talismanrc is empty", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockSHA256Hasher := mockutility.NewMockSHA256Hasher(ctrl) + ignoreConfig := talismanrc.NewTalismanRC(nil) + cc := NewChecksumCompare(nil, mockSHA256Hasher, ignoreConfig) + addition := gitrepo.Addition{Path: "some.txt"} + mockSHA256Hasher.EXPECT().CollectiveSHA256Hash([]string{string(addition.Path)}).Return("somesha") + + required := cc.IsScanNotRequired(addition) + + assert.False(t, required) + }) + + t.Run("should loop through talismanrc configs", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockSHA256Hasher := mockutility.NewMockSHA256Hasher(ctrl) + checksumCalculator := mockchecksumcalculator.NewMockChecksumCalculator(ctrl) + ignoreConfig := talismanrc.TalismanRC{ + FileIgnoreConfig: []talismanrc.FileIgnoreConfig{ + { + FileName: "some.txt", + Checksum: "sha1", + }, + }, + } + cc := NewChecksumCompare(checksumCalculator, mockSHA256Hasher, &ignoreConfig) + addition := gitrepo.Addition{Name: "some.txt",} + mockSHA256Hasher.EXPECT().CollectiveSHA256Hash([]string{string(addition.Path)}).Return("somesha") + checksumCalculator.EXPECT().CalculateCollectiveChecksumForPattern("some.txt").Return("sha1") + + required := cc.IsScanNotRequired(addition) + + assert.True(t, required) + }) -func TestShouldReturnEmptyFileHashWhenNoPathsPassed(t *testing.T) { - checksum := utility.CollectiveSHA256Hash([]string{}) - assert.Equal(t, checksum, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "Should be equal to empty hash value when no paths passed") } diff --git a/detector/detection_results.go b/detector/detection_results.go index 59155e42..710ba334 100644 --- a/detector/detection_results.go +++ b/detector/detection_results.go @@ -307,7 +307,7 @@ func (r *DetectionResults) suggestTalismanRC(fs afero.Fs, ignoreFile string, fil var entriesToAdd []talismanrc.FileIgnoreConfig for _, filePath := range filePaths { - currentChecksum := utility.CollectiveSHA256Hash([]string{filePath}) + currentChecksum := utility.DefaultSHA256Hasher{}.CollectiveSHA256Hash([]string{filePath}) fileIgnoreConfig := talismanrc.FileIgnoreConfig{filePath, currentChecksum, []string{}} entriesToAdd = append(entriesToAdd, fileIgnoreConfig) } diff --git a/detector/detector.go b/detector/detector.go index 7c67f4c5..d498e647 100644 --- a/detector/detector.go +++ b/detector/detector.go @@ -1,15 +1,18 @@ package detector import ( + "os" + "talisman/checksumcalculator" "talisman/gitrepo" "talisman/talismanrc" + "talisman/utility" ) //Detector represents a single kind of test to be performed against a set of Additions //Detectors are expected to honor the ignores that are passed in and log them in the results //Detectors are expected to signal any errors to the results type Detector interface { - Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) + Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) } //Chain represents a chain of Detectors. @@ -42,8 +45,14 @@ func (dc *Chain) AddDetector(d Detector) *Chain { //Test validates the additions against each detector in the chain. //The results are passed in from detector to detector and thus collect all errors from all detectors -func (dc *Chain) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { +func (dc *Chain) Test(currentAdditions []gitrepo.Addition, talismanRC *talismanrc.TalismanRC, result *DetectionResults) { + wd, _ := os.Getwd() + repo := gitrepo.RepoLocatedAt(wd) + allAdditions := repo.TrackedFilesAsAdditions() + hasher := utility.DefaultSHA256Hasher{} + calculator := checksumcalculator.NewChecksumCalculator(hasher, append(allAdditions, currentAdditions...)) + cc := NewChecksumCompare(calculator, hasher, talismanRC) for _, v := range dc.detectors { - v.Test(additions, ignoreConfig, result) + v.Test(cc, currentAdditions, talismanRC, result) } } diff --git a/detector/detector_test.go b/detector/detector_test.go index b1f1ccc3..03528d14 100644 --- a/detector/detector_test.go +++ b/detector/detector_test.go @@ -28,11 +28,11 @@ func TestValidationChainWithFailingValidationAlwaysFails(t *testing.T) { type FailingDetection struct{} -func (v FailingDetection) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { +func (v FailingDetection) Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { result.Fail("some_file", "filecontent", "FAILED BY DESIGN", []string{}) } type PassingDetection struct{} -func (p PassingDetection) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { +func (p PassingDetection) Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { } diff --git a/detector/filecontent_detector.go b/detector/filecontent_detector.go index b02df3c7..47f9eab9 100644 --- a/detector/filecontent_detector.go +++ b/detector/filecontent_detector.go @@ -72,7 +72,7 @@ type content struct { results []string } -func (fc *FileContentDetector) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { +func (fc *FileContentDetector) Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { contentTypes := []struct { contentType fn @@ -90,18 +90,17 @@ func (fc *FileContentDetector) Test(additions []gitrepo.Addition, ignoreConfig * fn: checkCreditCardNumber, }, } - cc := NewChecksumCompare(additions, ignoreConfig) re := regexp.MustCompile(`(?i)checksum[ \t]*:[ \t]*[0-9a-fA-F]+`) contents := make(chan content, 512) - ignoredFilePaths := make(chan gitrepo.FilePath, len(additions)) + ignoredFilePaths := make(chan gitrepo.FilePath, len(currentAdditions)) waitGroup := &sync.WaitGroup{} - waitGroup.Add(len(additions)) - for _, addition := range additions { + waitGroup.Add(len(currentAdditions)) + for _, addition := range currentAdditions { go func(addition gitrepo.Addition) { defer waitGroup.Done() - if ignoreConfig.Deny(addition, "filecontent") || cc.IsScanNotRequired(addition) { + if ignoreConfig.Deny(addition, "filecontent") || comparator.IsScanNotRequired(addition) { ignoredFilePaths <- addition.Path return } diff --git a/detector/filecontent_detector_test.go b/detector/filecontent_detector_test.go index 9ebfdb2f..c03cb829 100644 --- a/detector/filecontent_detector_test.go +++ b/detector/filecontent_detector_test.go @@ -5,6 +5,7 @@ import ( "strings" "talisman/gitrepo" "talisman/talismanrc" + "talisman/utility" "testing" "github.com/stretchr/testify/assert" @@ -21,7 +22,7 @@ func TestShouldNotFlagSafeText(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().Test(additions, &talismanrc.TalismanRC{}, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, &talismanrc.TalismanRC{}, results) assert.False(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -31,7 +32,7 @@ func TestShouldIgnoreFileIfNeeded(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().Test(additions, talismanrc.NewTalismanRC([]byte(talismanRCContents)), results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanrc.NewTalismanRC([]byte(talismanRCContents)), results) assert.True(t, results.Successful(), "Expected file %s to be ignored by pattern", filename) } @@ -45,7 +46,7 @@ func TestShouldNotFlag4CharSafeText(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.False(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -56,7 +57,7 @@ func TestShouldNotFlagLowEntropyBase64Text(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.False(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -68,7 +69,7 @@ func TestShouldFlagPotentialAWSSecretKeys(t *testing.T) { additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := fmt.Sprintf("Expected file to not to contain base64 encoded texts such as: %s", awsSecretAccessKey) assert.True(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") assert.Equal(t, expectedMessage, getFailureMessages(results, filePath)[0]) @@ -83,7 +84,7 @@ func TestShouldFlagPotentialSecretWithoutTrimmingWhenLengthLessThan50Characters( additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := fmt.Sprintf("Expected file to not to contain base64 encoded texts such as: %s", secret) assert.True(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") assert.Equal(t, expectedMessage, getFailureMessages(results, filePath)[0]) @@ -98,7 +99,7 @@ func TestShouldFlagPotentialJWT(t *testing.T) { additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := fmt.Sprintf("Expected file to not to contain base64 encoded texts such as: %s", jwt[:47]+"...") assert.True(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") assert.Equal(t, expectedMessage, getFailureMessages(results, filePath)[0]) @@ -113,7 +114,7 @@ func TestShouldFlagPotentialSecretsWithinJavaCode(t *testing.T) { additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := "Expected file to not to contain base64 encoded texts such as: accessKey=\"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPL..." assert.True(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") assert.Equal(t, expectedMessage, getFailureMessages(results, filePath)[0]) @@ -127,7 +128,7 @@ func TestShouldNotFlagPotentialSecretsWithinSafeJavaCode(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.False(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -138,7 +139,7 @@ func TestShouldNotFlagPotentialSecretsWithinSafeLongMethodName(t *testing.T) { filename := "filename" additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.False(t, results.HasFailures(), "Expected file to not to contain base64 encoded texts") } @@ -150,7 +151,7 @@ func TestShouldFlagPotentialSecretsEncodedInHex(t *testing.T) { additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := "Expected file to not to contain hex encoded texts such as: " + hex assert.Equal(t, expectedMessage, getFailureMessages(results, filePath)[0]) assert.Len(t, results.Results, 1) @@ -166,7 +167,7 @@ func TestResultsShouldContainHexTextsIfHexAndBase64ExistInFile(t *testing.T) { additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := "Expected file to not to contain hex encoded texts such as: " + hex messageReceived := strings.Join(getFailureMessages(results, filePath), " ") assert.Regexp(t, expectedMessage, messageReceived, "Should contain hex detection message") @@ -183,7 +184,7 @@ func TestResultsShouldContainBase64TextsIfHexAndBase64ExistInFile(t *testing.T) additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := "Expected file to not to contain base64 encoded texts such as: " + base64 messageReceived := strings.Join(getFailureMessages(results, filePath), " ") assert.Regexp(t, expectedMessage, messageReceived, "Should contain base64 detection message") @@ -198,7 +199,7 @@ func TestResultsShouldContainCreditCardNumberIfCreditCardNumberExistInFile(t *te additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} filePath := additions[0].Path - NewFileContentDetector().Test(additions, talismanRC, results) + NewFileContentDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expectedMessage := "Expected file to not to contain credit card numbers such as: " + creditCardNumber assert.Equal(t, expectedMessage, getFailureMessages(results, filePath)[0]) assert.Len(t, results.Results, 1) diff --git a/detector/filename_detector.go b/detector/filename_detector.go index 0831c30d..5e192252 100644 --- a/detector/filename_detector.go +++ b/detector/filename_detector.go @@ -79,10 +79,9 @@ func NewFileNameDetector(patterns []*regexp.Regexp) Detector { } //Test tests the fileNames of the Additions to ensure that they don't look suspicious -func (fd FileNameDetector) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { - cc := NewChecksumCompare(additions, ignoreConfig) - for _, addition := range additions { - if ignoreConfig.Deny(addition, "filename") || cc.IsScanNotRequired(addition) { +func (fd FileNameDetector) Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { + for _, addition := range currentAdditions { + if ignoreConfig.Deny(addition, "filename") || comparator.IsScanNotRequired(addition) { log.WithFields(log.Fields{ "filePath": addition.Path, }).Info("Ignoring addition as it was specified to be ignored.") diff --git a/detector/filename_detector_test.go b/detector/filename_detector_test.go index 255766f3..d450a487 100644 --- a/detector/filename_detector_test.go +++ b/detector/filename_detector_test.go @@ -5,6 +5,7 @@ package detector import ( "regexp" + "talisman/utility" "testing" "talisman/gitrepo" @@ -152,20 +153,20 @@ func shouldNotFailWithDefaultDetectorAndIgnores(fileName, ignore string, t *test talismanRC.FileIgnoreConfig = make([]talismanrc.FileIgnoreConfig, 1) talismanRC.FileIgnoreConfig[0] = fileIgnoreConfig - DefaultFileNameDetector().Test(additionsNamed(fileName), talismanRC, results) + DefaultFileNameDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additionsNamed(fileName), talismanRC, results) assert.True(t, results.Successful(), "Expected file %s to be ignored by pattern", fileName, ignore) } func shouldFailWithSpecificPattern(fileName, pattern string, t *testing.T) { results := NewDetectionResults() pt := regexp.MustCompile(pattern) - NewFileNameDetector([]*regexp.Regexp{pt}).Test(additionsNamed(fileName), talismanRC, results) + NewFileNameDetector([]*regexp.Regexp{pt}).Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additionsNamed(fileName), talismanRC, results) assert.True(t, results.HasFailures(), "Expected file %s to fail the check against the %s pattern", fileName, pattern) } func shouldFailWithDefaultDetector(fileName, pattern string, t *testing.T) { results := NewDetectionResults() - DefaultFileNameDetector().Test(additionsNamed(fileName), talismanRC, results) + DefaultFileNameDetector().Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additionsNamed(fileName), talismanRC, results) assert.True(t, results.HasFailures(), "Expected file %s to fail the check against default detector. Missing pattern %s?", fileName, pattern) } diff --git a/detector/filesize_detector.go b/detector/filesize_detector.go index e697f8c0..0cde528a 100644 --- a/detector/filesize_detector.go +++ b/detector/filesize_detector.go @@ -2,7 +2,6 @@ package detector import ( "fmt" - "talisman/gitrepo" "talisman/talismanrc" @@ -13,18 +12,13 @@ type FileSizeDetector struct { size int } -func DefaultFileSizeDetector() Detector { - return NewFileSizeDetector(1 * 1024 * 1024) -} - func NewFileSizeDetector(size int) Detector { return FileSizeDetector{size} } -func (fd FileSizeDetector) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { - cc := NewChecksumCompare(additions, ignoreConfig) - for _, addition := range additions { - if ignoreConfig.Deny(addition, "filesize") || cc.IsScanNotRequired(addition) { +func (fd FileSizeDetector) Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { + for _, addition := range currentAdditions { + if ignoreConfig.Deny(addition, "filesize") || comparator.IsScanNotRequired(addition) { log.WithFields(log.Fields{ "filePath": addition.Path, }).Info("Ignoring addition as it was specified to be ignored.") diff --git a/detector/filesize_detector_test.go b/detector/filesize_detector_test.go index 317735e1..53c722a7 100644 --- a/detector/filesize_detector_test.go +++ b/detector/filesize_detector_test.go @@ -1,6 +1,7 @@ package detector import ( + "talisman/utility" "testing" "talisman/gitrepo" @@ -13,7 +14,7 @@ func TestShouldFlagLargeFiles(t *testing.T) { results := NewDetectionResults() content := []byte("more than one byte") additions := []gitrepo.Addition{gitrepo.NewAddition("filename", content)} - NewFileSizeDetector(2).Test(additions, talismanRC, results) + NewFileSizeDetector(2).Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.True(t, results.HasFailures(), "Expected file to fail the check against file size detector.") } @@ -21,7 +22,7 @@ func TestShouldNotFlagSmallFiles(t *testing.T) { results := NewDetectionResults() content := []byte("m") additions := []gitrepo.Addition{gitrepo.NewAddition("filename", content)} - NewFileSizeDetector(2).Test(additions, talismanRC, results) + NewFileSizeDetector(2).Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.False(t, results.HasFailures(), "Expected file to not to fail the check against file size detector.") } @@ -39,6 +40,6 @@ func TestShouldNotFlagIgnoredLargeFiles(t *testing.T) { talismanRC.FileIgnoreConfig[0] = fileIgnoreConfig additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewFileSizeDetector(2).Test(additions, talismanRC, results) + NewFileSizeDetector(2).Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) assert.True(t, results.Successful(), "expected file %s to be ignored by file size detector", filename) } diff --git a/detector/pattern_detector.go b/detector/pattern_detector.go index 4a63c6d8..c4938851 100644 --- a/detector/pattern_detector.go +++ b/detector/pattern_detector.go @@ -34,16 +34,15 @@ type match struct { } //Test tests the contents of the Additions to ensure that they don't look suspicious -func (detector PatternDetector) Test(additions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { - cc := NewChecksumCompare(additions, ignoreConfig) +func (detector PatternDetector) Test(comparator ChecksumCompare, currentAdditions []gitrepo.Addition, ignoreConfig *talismanrc.TalismanRC, result *DetectionResults) { matches := make(chan match, 512) ignoredFilePaths := make(chan gitrepo.FilePath, 512) waitGroup := &sync.WaitGroup{} - waitGroup.Add(len(additions)) - for _, addition := range additions { + waitGroup.Add(len(currentAdditions)) + for _, addition := range currentAdditions { go func(addition gitrepo.Addition) { defer waitGroup.Done() - if ignoreConfig.Deny(addition, "filecontent") || cc.IsScanNotRequired(addition) { + if ignoreConfig.Deny(addition, "filecontent") || comparator.IsScanNotRequired(addition) { ignoredFilePaths <- addition.Path return } diff --git a/detector/pattern_detector_test.go b/detector/pattern_detector_test.go index 118e383f..dab5b9e0 100644 --- a/detector/pattern_detector_test.go +++ b/detector/pattern_detector_test.go @@ -4,6 +4,7 @@ import ( "strings" "talisman/gitrepo" "talisman/talismanrc" + "talisman/utility" "testing" "github.com/stretchr/testify/assert" @@ -51,14 +52,14 @@ func TestShouldIgnorePasswordPatterns(t *testing.T) { fileIgnoreConfig := talismanrc.FileIgnoreConfig{filename, "833b6c24c8c2c5c7e1663226dc401b29c005492dc76a1150fc0e0f07f29d4cc3", []string{"filecontent"}} ignores := &talismanrc.TalismanRC{FileIgnoreConfig: []talismanrc.FileIgnoreConfig{fileIgnoreConfig}} - NewPatternDetector(customPatterns).Test(additions, ignores, results) + NewPatternDetector(customPatterns).Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, ignores, results) assert.True(t, results.Successful(), "Expected file %s to be ignored by pattern", filename) } func DetectionOfSecretPattern(filename string, content []byte) (*DetectionResults, []gitrepo.Addition, string) { results := NewDetectionResults() additions := []gitrepo.Addition{gitrepo.NewAddition(filename, content)} - NewPatternDetector(customPatterns).Test(additions, talismanRC, results) + NewPatternDetector(customPatterns).Test(NewChecksumCompare(nil, utility.DefaultSHA256Hasher{}, talismanrc.NewTalismanRC(nil)), additions, talismanRC, results) expected := "Potential secret pattern : " + string(content) return results, additions, expected } diff --git a/internal/mock/checksumcalculator/checksumcalculator.go b/internal/mock/checksumcalculator/checksumcalculator.go new file mode 100644 index 00000000..9cb93848 --- /dev/null +++ b/internal/mock/checksumcalculator/checksumcalculator.go @@ -0,0 +1,61 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: checksumcalculator/checksumcalculator.go + +// Package mock is a generated GoMock package. +package mock + +import ( + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockChecksumCalculator is a mock of ChecksumCalculator interface +type MockChecksumCalculator struct { + ctrl *gomock.Controller + recorder *MockChecksumCalculatorMockRecorder +} + +// MockChecksumCalculatorMockRecorder is the mock recorder for MockChecksumCalculator +type MockChecksumCalculatorMockRecorder struct { + mock *MockChecksumCalculator +} + +// NewMockChecksumCalculator creates a new mock instance +func NewMockChecksumCalculator(ctrl *gomock.Controller) *MockChecksumCalculator { + mock := &MockChecksumCalculator{ctrl: ctrl} + mock.recorder = &MockChecksumCalculatorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockChecksumCalculator) EXPECT() *MockChecksumCalculatorMockRecorder { + return m.recorder +} + +// SuggestTalismanRC mocks base method +func (m *MockChecksumCalculator) SuggestTalismanRC(fileNamePatterns []string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SuggestTalismanRC", fileNamePatterns) + ret0, _ := ret[0].(string) + return ret0 +} + +// SuggestTalismanRC indicates an expected call of SuggestTalismanRC +func (mr *MockChecksumCalculatorMockRecorder) SuggestTalismanRC(fileNamePatterns interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SuggestTalismanRC", reflect.TypeOf((*MockChecksumCalculator)(nil).SuggestTalismanRC), fileNamePatterns) +} + +// CalculateCollectiveChecksumForPattern mocks base method +func (m *MockChecksumCalculator) CalculateCollectiveChecksumForPattern(fileNamePattern string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CalculateCollectiveChecksumForPattern", fileNamePattern) + ret0, _ := ret[0].(string) + return ret0 +} + +// CalculateCollectiveChecksumForPattern indicates an expected call of CalculateCollectiveChecksumForPattern +func (mr *MockChecksumCalculatorMockRecorder) CalculateCollectiveChecksumForPattern(fileNamePattern interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateCollectiveChecksumForPattern", reflect.TypeOf((*MockChecksumCalculator)(nil).CalculateCollectiveChecksumForPattern), fileNamePattern) +} diff --git a/internal/mock/utility/sha_256_hasher.go b/internal/mock/utility/sha_256_hasher.go new file mode 100644 index 00000000..32bc85f7 --- /dev/null +++ b/internal/mock/utility/sha_256_hasher.go @@ -0,0 +1,47 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: utility/sha_256_hasher.go + +// Package mock is a generated GoMock package. +package mock + +import ( + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockSHA256Hasher is a mock of SHA256Hasher interface +type MockSHA256Hasher struct { + ctrl *gomock.Controller + recorder *MockSHA256HasherMockRecorder +} + +// MockSHA256HasherMockRecorder is the mock recorder for MockSHA256Hasher +type MockSHA256HasherMockRecorder struct { + mock *MockSHA256Hasher +} + +// NewMockSHA256Hasher creates a new mock instance +func NewMockSHA256Hasher(ctrl *gomock.Controller) *MockSHA256Hasher { + mock := &MockSHA256Hasher{ctrl: ctrl} + mock.recorder = &MockSHA256HasherMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockSHA256Hasher) EXPECT() *MockSHA256HasherMockRecorder { + return m.recorder +} + +// CollectiveSHA256Hash mocks base method +func (m *MockSHA256Hasher) CollectiveSHA256Hash(paths []string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CollectiveSHA256Hash", paths) + ret0, _ := ret[0].(string) + return ret0 +} + +// CollectiveSHA256Hash indicates an expected call of CollectiveSHA256Hash +func (mr *MockSHA256HasherMockRecorder) CollectiveSHA256Hash(paths interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CollectiveSHA256Hash", reflect.TypeOf((*MockSHA256Hasher)(nil).CollectiveSHA256Hash), paths) +} diff --git a/runner.go b/runner.go index 819c1e5c..bfccacff 100644 --- a/runner.go +++ b/runner.go @@ -71,7 +71,7 @@ func (r *Runner) RunChecksumCalculator(fileNamePatterns []string) int { gitTrackedFilesAsAdditions := repo.TrackedFilesAsAdditions() //Adding staged files for calculation gitTrackedFilesAsAdditions = append(gitTrackedFilesAsAdditions, repo.StagedAdditions()...) - cc := checksumcalculator.NewChecksumCalculator(gitTrackedFilesAsAdditions) + cc := checksumcalculator.NewChecksumCalculator(utility.DefaultSHA256Hasher{}, gitTrackedFilesAsAdditions) rcSuggestion := cc.SuggestTalismanRC(fileNamePatterns) if rcSuggestion != "" { fmt.Print(rcSuggestion) diff --git a/utility/sha_256_hasher.go b/utility/sha_256_hasher.go new file mode 100644 index 00000000..b6f513fd --- /dev/null +++ b/utility/sha_256_hasher.go @@ -0,0 +1,37 @@ +package utility + +import ( + "crypto/sha256" + "encoding/hex" + "io/ioutil" +) + +type SHA256Hasher interface { + CollectiveSHA256Hash(paths []string) string +} + +type DefaultSHA256Hasher struct {} + +//CollectiveSHA256Hash return collective sha256 hash of the passed paths +func (DefaultSHA256Hasher) CollectiveSHA256Hash(paths []string) string { + var finHash = "" + for _, path := range paths { + sbyte := []byte(finHash) + concatBytes := hashByte(&sbyte) + nameByte := []byte(path) + nameHash := hashByte(&nameByte) + fileBytes, _ := ioutil.ReadFile(path) + fileHash := hashByte(&fileBytes) + finHash = concatBytes + fileHash + nameHash + } + c := []byte(finHash) + m := hashByte(&c) + return m +} + +func hashByte(contentPtr *[]byte) string { + contents := *contentPtr + hasher := sha256.New() + hasher.Write(contents) + return hex.EncodeToString(hasher.Sum(nil)) +} diff --git a/utility/sha_256_hasher_test.go b/utility/sha_256_hasher_test.go new file mode 100644 index 00000000..eed0da3f --- /dev/null +++ b/utility/sha_256_hasher_test.go @@ -0,0 +1,20 @@ +package utility + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestShouldReturnCorrectFileHash(t *testing.T) { + hasher := DefaultSHA256Hasher{} + checksumSomeFile := hasher.CollectiveSHA256Hash([]string{"some_file.pem"}) + checksumTestSomeFile := hasher.CollectiveSHA256Hash([]string{"test/some_file.pem"}) + assert.Equal(t, checksumSomeFile, "87139cc4d975333b25b6275f97680604add51b84eb8f4a3b9dcbbc652e6f27ac", "Should be equal to some_file.pem hash value") + assert.Equal(t, checksumTestSomeFile, "25bd31a28bf9d4e06327f1c4a5cab2260574ae508803f66adcc393350e994866", "Should be equal to test/some_file.pem hash value") +} + +func TestShouldReturnEmptyFileHashWhenNoPathsPassed(t *testing.T) { + hasher := DefaultSHA256Hasher{} + checksum := hasher.CollectiveSHA256Hash([]string{}) + assert.Equal(t, checksum, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "Should be equal to empty hash value when no paths passed") +} diff --git a/utility/utility.go b/utility/utility.go index 8fdc33df..7dd57be7 100644 --- a/utility/utility.go +++ b/utility/utility.go @@ -1,8 +1,6 @@ package utility import ( - "crypto/sha256" - "encoding/hex" "fmt" "io" "io/ioutil" @@ -25,30 +23,6 @@ func UniqueItems(stringSlice []string) []string { return list } -//CollectiveSHA256Hash return collective sha256 hash of the passed paths -func CollectiveSHA256Hash(paths []string) string { - var finHash = "" - for _, path := range paths { - sbyte := []byte(finHash) - concatBytes := hashByte(&sbyte) - nameByte := []byte(path) - nameHash := hashByte(&nameByte) - fileBytes, _ := ioutil.ReadFile(path) - fileHash := hashByte(&fileBytes) - finHash = concatBytes + fileHash + nameHash - } - c := []byte(finHash) - m := hashByte(&c) - return m -} - -func hashByte(contentPtr *[]byte) string { - contents := *contentPtr - hasher := sha256.New() - hasher.Write(contents) - return hex.EncodeToString(hasher.Sum(nil)) -} - //Creates art for console output func CreateArt(msg string) { myFigure := figure.NewFigure(msg, "basic", true)