// == WARNING == // These functions are used to generate GitLeak's default config. // You are free to use these in your own project, HOWEVER, no API stability is guaranteed. package utils import ( "fmt" "strings" "github.com/zricethezav/gitleaks/v8/regexp" ) const ( // case insensitive prefix caseInsensitive = `(?i)` // identifier prefix (just an ignore group) identifierCaseInsensitivePrefix = `[\w.-]{0,50}?(?i:` identifierCaseInsensitiveSuffix = `)` identifierPrefix = `[\w.-]{0,50}?(?:` identifierSuffix = `)(?:[ \t\w.-]{0,20})[\s'"|]{0,3}` // commonly used assignment operators or function call //language=regexp operator = `(?:=|>|:{1,3}=|\|\|:|<=|=>|:|\?=|,)` // boundaries for the secret secretPrefixUnique = `\b(` secretPrefix = `[\x60'"\s=]{0,5}(` secretSuffix = `)(?:[\x60'"\s;]|$)` ) func GenerateSemiGenericRegex(identifiers []string, secretRegex string, isCaseInsensitive bool) *regexp.Regexp { var sb strings.Builder // The identifiers should always be case-insensitive. // This is inelegant but prevents an extraneous `(?i:)` from being added to the pattern; it could be removed. if isCaseInsensitive { sb.WriteString(caseInsensitive) writeIdentifiers(&sb, identifiers) } else { sb.WriteString(identifierCaseInsensitivePrefix) writeIdentifiers(&sb, identifiers) sb.WriteString(identifierCaseInsensitiveSuffix) } sb.WriteString(operator) sb.WriteString(secretPrefix) sb.WriteString(secretRegex) sb.WriteString(secretSuffix) return regexp.MustCompile(sb.String()) } func MergeRegexps(regexps ...*regexp.Regexp) *regexp.Regexp { patterns := make([]string, len(regexps)) for i, r := range regexps { patterns[i] = r.String() } return regexp.MustCompile(strings.Join(patterns, "|")) } func writeIdentifiers(sb *strings.Builder, identifiers []string) { sb.WriteString(identifierPrefix) sb.WriteString(strings.Join(identifiers, "|")) sb.WriteString(identifierSuffix) } func GenerateUniqueTokenRegex(secretRegex string, isCaseInsensitive bool) *regexp.Regexp { var sb strings.Builder if isCaseInsensitive { sb.WriteString(caseInsensitive) } sb.WriteString(secretPrefixUnique) sb.WriteString(secretRegex) sb.WriteString(secretSuffix) return regexp.MustCompile(sb.String()) } func GenerateSampleSecret(identifier string, secret string) string { return fmt.Sprintf("%s_api_token = \"%s\"", identifier, secret) } // See: https://github.com/gitleaks/gitleaks/issues/1222 func GenerateSampleSecrets(identifier string, secret string) []string { samples := map[string]string{ // Configuration // INI "ini - quoted1": "{i}Token=\"{s}\"", "ini - quoted2": "{i}Token = \"{s}\"", "ini - unquoted1": "{i}Token={s}", "ini - unquoted2": "{i}Token = {s}", // JSON "json - string": "{\n \"{i}_token\": \"{s}\"\n}", // TODO: "json - escaped string": "\\{\n \\\"{i}_token\\\": \\\"{s}\\\"\n\\}", // TODO: "json - string key/value": "{\n \"name\": \"{i}_token\",\n \"value\": \"{s}\"\n}", // XML // TODO: "xml - element": "<{i}Token>{s}", "xml - element multiline": "<{i}Token>\n {s}\n", // TODO: "xml - attribute": "", // TODO: "xml - key/value elements": "\n \n \n", // YAML "yaml - singleline - unquoted": "{i}_token: {s}", "yaml - singleline - single quote": "{i}_token: '{s}'", "yaml - singleline - double quote": "{i}_token: \"{s}\"", // TODO: "yaml - multiline - literal": "{i}_token: |\n {s}", // TODO: "yaml - multiline - folding": "{i}_token: >\n {s}", // "": "", // Programming Languages "C#": `string {i}Token = "{s}";`, "go - normal": `var {i}Token string = "{s}"`, "go - short": `{i}Token := "{s}"`, "go - backticks": "{i}Token := `{s}`", "java": "String {i}Token = \"{s}\";", // TODO: "java - escaped quotes": `config.put("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"JDOE35\" {i}Token=\"{s}\""`, // TODO:"kotlin - type": "var {i}Token: string = \"{s}\"", "kotlin - notype": "var {i}Token = \"{s}\"", "php - string concat": `${i}Token .= "{s}"`, // TODO: "php - null coalesce": `${i}Token ??= "{s}"`, "python - single quote": "{i}Token = '{s}'", "python - double quote": `{i}Token = "{s}"`, // "": "", // Miscellaneous // TODO: "url - basic auth": `https://{i}:{s}@example.com/`, // TODO: "url - query parameter": "https://example.com?{i}Token={s}&fooBar=baz", // TODO: "comment - slash": "//{s} is the password", // TODO: "comment - slash multiline": "/*{s} is the password", // TODO: "comment - hashtag": "#{s} is the password", // TODO: "comment - semicolon": ";{s} is the password", // TODO: "csv - unquoted": `{i}Token,{s},`, "misc - comma operator": `System.setProperty("{I}_TOKEN", "{s}")`, // TODO: "misc - comma suffix": `environmentVariables = {PATH=/usr/local/bin, ENV=/etc/bashrc, {I}_PASSWORD={s}, LC_CTYPE=en_CA.UTF-8},`, // Spotted in `./Library/Logs/gradle-kotlin-dsl-resolver-xxxx.log` "logstash": " \"{i}Token\" => \"{s}\"", // TODO: "sql - tabular": "|{s}|", // TODO: "sql": "", // Makefile // See: https://github.com/gitleaks/gitleaks/pull/1191 "make - recursive assignment": "{i}_TOKEN = \"{s}\"", "make - simple assignment": "{i}_TOKEN := \"{s}\"", "make - shell assignment": "{i}_TOKEN ::= \"{s}\"", "make - evaluated shell assignment": "{i}_TOKEN :::= \"{s}\"", "make - conditional assignment": "{i}_TOKEN ?= \"{s}\"", // TODO: "make - append": "{i}_TOKEN += \"{s}\"", // "": "", } replacer := strings.NewReplacer( "{i}", identifier, "{I}", strings.ToUpper(identifier), "{s}", secret, ) cases := make([]string, 0, len(samples)) for _, v := range samples { cases = append(cases, replacer.Replace(v)) } return cases }