mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2024-11-10 07:04:24 +00:00
Support azure git links (#1662)
* Support azure git links. * update comment. * update test names.
This commit is contained in:
parent
f2bfcc7ac6
commit
4f4a79f62b
6 changed files with 133 additions and 74 deletions
|
@ -2,6 +2,7 @@ package giturl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -13,8 +14,29 @@ const (
|
||||||
providerGithub provider = "Github"
|
providerGithub provider = "Github"
|
||||||
providerGitlab provider = "Gitlab"
|
providerGitlab provider = "Gitlab"
|
||||||
providerBitbucket provider = "Bitbucket"
|
providerBitbucket provider = "Bitbucket"
|
||||||
|
providerAzure provider = "Azure"
|
||||||
|
|
||||||
|
urlGithub = "github.com/"
|
||||||
|
urlGitlab = "gitlab.com/"
|
||||||
|
urlBitbucket = "bitbucket.org/"
|
||||||
|
urlAzure = "dev.azure.com/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func determineProvider(repo string) provider {
|
||||||
|
switch {
|
||||||
|
case strings.Contains(repo, urlGithub):
|
||||||
|
return providerGithub
|
||||||
|
case strings.Contains(repo, urlGitlab):
|
||||||
|
return providerGitlab
|
||||||
|
case strings.Contains(repo, urlBitbucket):
|
||||||
|
return providerBitbucket
|
||||||
|
case strings.Contains(repo, urlAzure):
|
||||||
|
return providerAzure
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NormalizeBitbucketRepo(repoURL string) (string, error) {
|
func NormalizeBitbucketRepo(repoURL string) (string, error) {
|
||||||
if !strings.HasPrefix(repoURL, "https") {
|
if !strings.HasPrefix(repoURL, "https") {
|
||||||
return "", errors.New("Bitbucket requires https repo urls: e.g. https://bitbucket.org/org/repo.git")
|
return "", errors.New("Bitbucket requires https repo urls: e.g. https://bitbucket.org/org/repo.git")
|
||||||
|
@ -88,3 +110,35 @@ func NormalizeOrgRepoURL(provider provider, repoURL string) (string, error) {
|
||||||
parsed.Path += ".git"
|
parsed.Path += ".git"
|
||||||
return parsed.String(), nil
|
return parsed.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateLink crafts a link to the specific file from a commit.
|
||||||
|
// Supports GitHub, GitLab, Bitbucket, and Azure Repos.
|
||||||
|
// If the provider supports hyperlinks to specific lines, the line number will be included.
|
||||||
|
func GenerateLink(repo, commit, file string, line int64) string {
|
||||||
|
switch determineProvider(repo) {
|
||||||
|
case providerBitbucket:
|
||||||
|
return repo[:len(repo)-4] + "/commits/" + commit
|
||||||
|
|
||||||
|
case providerGithub, providerGitlab:
|
||||||
|
var baseLink string
|
||||||
|
if file == "" {
|
||||||
|
baseLink = repo[:len(repo)-4] + "/commit/" + commit
|
||||||
|
} else {
|
||||||
|
baseLink = repo[:len(repo)-4] + "/blob/" + commit + "/" + file
|
||||||
|
if line > 0 {
|
||||||
|
baseLink += "#L" + strconv.FormatInt(line, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return baseLink
|
||||||
|
|
||||||
|
case providerAzure:
|
||||||
|
baseLink := repo + "?path=" + file + "&version=GB" + commit
|
||||||
|
if line > 0 {
|
||||||
|
baseLink += "&line=" + strconv.FormatInt(line, 10)
|
||||||
|
}
|
||||||
|
return baseLink
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_NormalizeOrgRepoURL(t *testing.T) {
|
func Test_NormalizeOrgRepoURL(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
Provider provider
|
Provider provider
|
||||||
Repo string
|
Repo string
|
||||||
|
@ -43,6 +45,8 @@ func Test_NormalizeOrgRepoURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_NormalizeBitbucketRepo(t *testing.T) {
|
func Test_NormalizeBitbucketRepo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
Repo string
|
Repo string
|
||||||
Out string
|
Out string
|
||||||
|
@ -69,6 +73,8 @@ func Test_NormalizeBitbucketRepo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_NormalizeGitlabRepo(t *testing.T) {
|
func Test_NormalizeGitlabRepo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
Repo string
|
Repo string
|
||||||
Out string
|
Out string
|
||||||
|
@ -93,3 +99,73 @@ func Test_NormalizeGitlabRepo(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateLink(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
repo string
|
||||||
|
commit string
|
||||||
|
file string
|
||||||
|
line int64
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "github link gen",
|
||||||
|
args: args{
|
||||||
|
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
||||||
|
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||||
|
file: ".gitignore",
|
||||||
|
},
|
||||||
|
want: "https://github.com/trufflesec-julian/confluence-go-api/blob/047b4a2ba42fc5b6c0bd535c5307434a666db5ec/.gitignore",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "github link gen with line",
|
||||||
|
args: args{
|
||||||
|
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
||||||
|
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||||
|
file: ".gitignore",
|
||||||
|
line: int64(4),
|
||||||
|
},
|
||||||
|
want: "https://github.com/trufflesec-julian/confluence-go-api/blob/047b4a2ba42fc5b6c0bd535c5307434a666db5ec/.gitignore#L4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "github link gen - no file",
|
||||||
|
args: args{
|
||||||
|
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
||||||
|
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||||
|
},
|
||||||
|
want: "https://github.com/trufflesec-julian/confluence-go-api/commit/047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Azure link gen",
|
||||||
|
args: args{
|
||||||
|
repo: "https://dev.azure.com/org/project/_git/repo",
|
||||||
|
commit: "abcdef",
|
||||||
|
file: "main.go",
|
||||||
|
},
|
||||||
|
want: "https://dev.azure.com/org/project/_git/repo?path=main.go&version=GBabcdef",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Azure link gen with line",
|
||||||
|
args: args{
|
||||||
|
repo: "https://dev.azure.com/org/project/_git/repo",
|
||||||
|
commit: "abcdef",
|
||||||
|
file: "main.go",
|
||||||
|
line: int64(20),
|
||||||
|
},
|
||||||
|
want: "https://dev.azure.com/org/project/_git/repo?path=main.go&version=GBabcdef&line=20",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := GenerateLink(tt.args.repo, tt.args.commit, tt.args.file, tt.args.line); got != tt.want {
|
||||||
|
t.Errorf("generateLink() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -693,28 +692,6 @@ func normalizeConfig(scanOptions *ScanOptions, repo *git.Repository) (err error)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateLink crafts a link to the specific file from a commit. This works in most major git providers (Github/Gitlab)
|
|
||||||
func GenerateLink(repo, commit, file string, line int64) string {
|
|
||||||
// bitbucket links are commits not commit...
|
|
||||||
if strings.Contains(repo, "bitbucket.org/") {
|
|
||||||
return repo[:len(repo)-4] + "/commits/" + commit
|
|
||||||
}
|
|
||||||
var link string
|
|
||||||
if file == "" {
|
|
||||||
link = repo[:len(repo)-4] + "/commit/" + commit
|
|
||||||
} else {
|
|
||||||
link = repo[:len(repo)-4] + "/blob/" + commit + "/" + file
|
|
||||||
|
|
||||||
// Both GitHub and Gitlab support hyperlinking to a specific line with #L<number>, e.g.:
|
|
||||||
// https://github.com/trufflesecurity/trufflehog/blob/e856a6890d0da5a218f4f9283500b80043884641/go.mod#L169
|
|
||||||
// https://gitlab.com/pdftk-java/pdftk/-/blob/88559a08f34175b6fae76c40a88f0377f64a12d7/java/com/gitlab/pdftk_java/report.java#L893
|
|
||||||
if line > 0 && (strings.Contains(repo, "github") || strings.Contains(repo, "gitlab")) {
|
|
||||||
link += "#L" + strconv.FormatInt(line, 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return link
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripPassword(u string) (string, error) {
|
func stripPassword(u string) (string, error) {
|
||||||
if strings.HasPrefix(u, "git@") {
|
if strings.HasPrefix(u, "git@") {
|
||||||
return u, nil
|
return u, nil
|
||||||
|
|
|
@ -151,55 +151,6 @@ func TestSource_Scan(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_generateLink(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
repo string
|
|
||||||
commit string
|
|
||||||
file string
|
|
||||||
line int64
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "test link gen",
|
|
||||||
args: args{
|
|
||||||
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
|
||||||
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
|
||||||
file: ".gitignore",
|
|
||||||
},
|
|
||||||
want: "https://github.com/trufflesec-julian/confluence-go-api/blob/047b4a2ba42fc5b6c0bd535c5307434a666db5ec/.gitignore",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test link gen",
|
|
||||||
args: args{
|
|
||||||
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
|
||||||
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
|
||||||
file: ".gitignore",
|
|
||||||
line: int64(4),
|
|
||||||
},
|
|
||||||
want: "https://github.com/trufflesec-julian/confluence-go-api/blob/047b4a2ba42fc5b6c0bd535c5307434a666db5ec/.gitignore#L4",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test link gen - no file",
|
|
||||||
args: args{
|
|
||||||
repo: "https://github.com/trufflesec-julian/confluence-go-api.git",
|
|
||||||
commit: "047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
|
||||||
},
|
|
||||||
want: "https://github.com/trufflesec-julian/confluence-go-api/commit/047b4a2ba42fc5b6c0bd535c5307434a666db5ec",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := GenerateLink(tt.args.repo, tt.args.commit, tt.args.file, tt.args.line); got != tt.want {
|
|
||||||
t.Errorf("generateLink() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We ran into an issue where upgrading a dependency caused the git patch chunking to break
|
// We ran into an issue where upgrading a dependency caused the git patch chunking to break
|
||||||
// So this test exists to make sure that when something changes, we know about it.
|
// So this test exists to make sure that when something changes, we know about it.
|
||||||
func TestSource_Chunks_Integration(t *testing.T) {
|
func TestSource_Chunks_Integration(t *testing.T) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/cache/memory"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/cache/memory"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
|
||||||
|
"github.com/trufflesecurity/trufflehog/v3/pkg/giturl"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/credentialspb"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
|
||||||
|
@ -261,7 +262,7 @@ func (s *Source) Init(aCtx context.Context, name string, jobID, sourceID int64,
|
||||||
File: sanitizer.UTF8(file),
|
File: sanitizer.UTF8(file),
|
||||||
Email: sanitizer.UTF8(email),
|
Email: sanitizer.UTF8(email),
|
||||||
Repository: sanitizer.UTF8(repository),
|
Repository: sanitizer.UTF8(repository),
|
||||||
Link: git.GenerateLink(repository, commit, file, line),
|
Link: giturl.GenerateLink(repository, commit, file, line),
|
||||||
Timestamp: sanitizer.UTF8(timestamp),
|
Timestamp: sanitizer.UTF8(timestamp),
|
||||||
Line: line,
|
Line: line,
|
||||||
Visibility: s.visibilityOf(aCtx, repository),
|
Visibility: s.visibilityOf(aCtx, repository),
|
||||||
|
|
|
@ -127,7 +127,7 @@ func (s *Source) Init(_ context.Context, name string, jobId, sourceId int64, ver
|
||||||
File: sanitizer.UTF8(file),
|
File: sanitizer.UTF8(file),
|
||||||
Email: sanitizer.UTF8(email),
|
Email: sanitizer.UTF8(email),
|
||||||
Repository: sanitizer.UTF8(repository),
|
Repository: sanitizer.UTF8(repository),
|
||||||
Link: git.GenerateLink(repository, commit, file, line),
|
Link: giturl.GenerateLink(repository, commit, file, line),
|
||||||
Timestamp: sanitizer.UTF8(timestamp),
|
Timestamp: sanitizer.UTF8(timestamp),
|
||||||
Line: line,
|
Line: line,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue