From 4410bd5353adccb508100a3d1660aee9c93289fa Mon Sep 17 00:00:00 2001 From: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> Date: Thu, 18 May 2023 10:09:26 -0700 Subject: [PATCH] fix: multiline output (#65) fixes #64 --- .github/dependabot.yml | 16 ++- .github/release-drafter.yml | 79 +++++++++++++++ .github/workflows/dogfood.yml | 6 +- .github/workflows/release-draft.yml | 31 ++++++ .github/workflows/release-publish.yml | 37 +++++++ .gitleaks/UDMSecretChecks.toml | 141 ++++++++++++++------------ LICENSE | 2 +- README.md | 96 ++++++++++++++---- entrypoint.sh | 27 ++--- 9 files changed, 338 insertions(+), 97 deletions(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-draft.yml create mode 100644 .github/workflows/release-publish.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a41b201..6fe88f4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,15 +1,27 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + version: 2 updates: + # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot + # Configuration for github-actions - package-ecosystem: github-actions directory: / schedule: interval: daily commit-message: - prefix: "[github-actions] " + prefix: "⬆️ github-actions" + include: scope + labels: + - dependencies + # Configuration for docker - package-ecosystem: docker directory: / schedule: interval: daily commit-message: - prefix: "[docker] " + prefix: "⬆️ docker" + include: scope + labels: + - dependencies diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..90c87ed --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,79 @@ +--- +name-template: v$RESOLVED_VERSION +tag-template: v$RESOLVED_VERSION +footer: | + + See details of all code changes: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION since previous release. +template: | + ## 🔄️ What's Changed + + $CHANGES + + ## 👥 Contributors + + $CONTRIBUTORS + +categories: + - title: 💥 Breaking + labels: + - major + - breaking + - title: 🚀 Features + labels: + - minor + - feature + - enhancement + - title: 🐛 Bug Fixes + labels: + - fix + - bugfix + - bug + - title: 🧰 Maintenance + labels: + - maintenance + - chore + - title: 📚 Documentation + labels: + - doc + - docs + - documentation + - title: Other changes + label: patch + - title: ⬆️ Dependencies + labels: + - dependencies + - deps + collapse-after: 3 + - title: 🚨 Security + label: security + collapse-after: 3 +exclude-labels: + - skip-changelog +change-template: "- $TITLE @$AUTHOR (#$NUMBER)" +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - major + - breaking + minor: + labels: + - minor + - feature + - enhancement + patch: + labels: + - patch + - fix + - bugfix + - bug + - maintenance + - chore + - doc + - docs + - documentation + - deps + - dependencies + - security + default: patch +# TODO autolabeler https://github.com/release-drafter/release-drafter/#autolabeler diff --git a/.github/workflows/dogfood.yml b/.github/workflows/dogfood.yml index 27e42f4..4f48276 100644 --- a/.github/workflows/dogfood.yml +++ b/.github/workflows/dogfood.yml @@ -4,7 +4,7 @@ on: [push, pull_request, pull_request_target, workflow_dispatch] # Allow one concurrent deployment concurrency: - group: ${{ github.event_name }}-${{ github.base_ref }}-${{ github.head_ref || github.event.number }} + group: ${{ format('{0}-{1}-{2}-{3}-{4}', github.workflow, github.event_name, github.ref, github.base_ref, github.head_ref) }} cancel-in-progress: true jobs: @@ -46,9 +46,11 @@ jobs: run: | echo "exitcode: ${{ steps.gitleaks.outputs.exitcode }}" echo "result: ${{ steps.gitleaks.outputs.result }}" - echo "output: ${{ steps.gitleaks.outputs.output }}" echo "command: ${{ steps.gitleaks.outputs.command }}" echo "report: ${{ steps.gitleaks.outputs.report }}" + echo "output: ${GITLEAKS_OUTPUT}" + env: + GITLEAKS_OUTPUT: ${{ steps.gitleaks.outputs.output }} - name: Upload SARIF report if: ${{ steps.gitleaks.outputs.exitcode == 1 }} diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml new file mode 100644 index 0000000..9dcf1e3 --- /dev/null +++ b/.github/workflows/release-draft.yml @@ -0,0 +1,31 @@ +--- +name: Draft Release + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + draft-release: + name: Draft Release + permissions: + contents: write + pull-requests: read + runs-on: ubuntu-latest + steps: + - name: 📝 Run Release Drafter + uses: release-drafter/release-drafter@v5 + id: release-drafter + with: + prerelease: true + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: ✍️ Post Summary + run: | + echo "${RELEASE_URL}" + echo "${RELEASE_URL}" >> "$GITHUB_STEP_SUMMARY" + env: + RELEASE_URL: ${{ steps.release-drafter.outputs.html_url }} diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml new file mode 100644 index 0000000..fe67aa3 --- /dev/null +++ b/.github/workflows/release-publish.yml @@ -0,0 +1,37 @@ +--- +name: Publish Release + +on: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release + release: + types: + - published + workflow_dispatch: + inputs: + tag_name: + description: Tag name that the major tag will point to + required: true + +env: + TAG_NAME: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} + +jobs: + gh-release: + name: Publish Release + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: 🥇 Update release to the latest + id: gh-release + uses: softprops/action-gh-release@v1 + with: + prerelease: false + token: ${{ github.token }} + + - name: 🥇 Update the major tag to include the ${{ env.TAG_NAME }} changes + id: major-tag + uses: actions/publish-action@v0.2.2 + with: + source-tag: ${{ env.TAG_NAME }} + token: ${{ github.token }} diff --git a/.gitleaks/UDMSecretChecks.toml b/.gitleaks/UDMSecretChecks.toml index 94502d2..333d4a4 100644 --- a/.gitleaks/UDMSecretChecks.toml +++ b/.gitleaks/UDMSecretChecks.toml @@ -1,10 +1,11 @@ -title = "gitleaks config" +title = "Gitleaks config" [extend] # useDefault will extend the base configuration with the default gitleaks config: # https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml useDefault = true +# rules based on: https://www.powershellgallery.com/packages/AzSK.AzureDevOps/0.9.9/Content/Framework%5CConfigurations%5CSVT%5CAzureDevOps%5CCredentialPatterns.xml [[rules]] id = "CSCAN0210" description = "GitCredential" @@ -18,13 +19,13 @@ regex = '''.''' path = '''\.keystore$''' [[rules]] -id = "CSCAN0020" +id = "CSCAN0020-1" description = "Base64EncodedCertificateInCode" regex = '''['">;=]MII[a-z0-9/+]{200}''' path = '''\.(?:cs|ini|json|ps1|publishsettings|template|trd|ts|xml)$''' [[rules]] -id = "CSCAN0020" +id = "CSCAN0020-2" description = "Base64EncodedCertificateInFile" regex = '''MII[A-Za-z0-9/+]{60}''' path = '''\.(?:cert|cer)$''' @@ -41,14 +42,14 @@ regex = [ ] [[rules]] -id = "CSCAN0060 1" -description = "PemFile 1" +id = "CSCAN0060" +description = "PemFile" path = '''\.pem$''' regex = '''-{5}BEGIN(?: (?:[dr]sa|ec|openssh))? PRIVATE KEY-{5}''' [[rules]] -id = "CSCAN0091 1" -description = "AspNetMachineKeyInConfig 1" +id = "CSCAN0091-1" +description = "AspNetMachineKeyInConfig1" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = ''']+(?:decryptionKey\s*\=\s*"[a-fA-F0-9]{48,}|validationKey\s*\=\s*"[a-fA-F0-9]{48,})[^>]+>''' [rules.allowlist] @@ -58,8 +59,8 @@ regex = [ ] [[rules]] -id = "CSCAN0091 2" -description = "AspNetMachineKeyInConfig 2" +id = "CSCAN0091-2" +description = "AspNetMachineKeyInConfig2" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''(?:decryptionKey|validationKey)="[a-zA-Z0-9]+"''' [rules.allowlist] @@ -69,16 +70,16 @@ regex = [ ] [[rules]] -id = "CSCAN0092 1" -description = "SqlConnectionStringInConfig 1" +id = "CSCAN0092-1" +description = "SqlConnectionStringInConfig1" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''(?i)(?:connection[sS]tring|connString)[^=]*=["'][^"']*[pP]assword\s*=\s*[^\s;][^"']*(?:'|")''' [rules.allowlist] regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' [[rules]] -id = "CSCAN0092 / CSCAN0043" -description = "SqlConnectionStringInConfig 2 / SqlConnectionStringInCode" +id = "CSCAN0092-2" +description = "SqlConnectionStringInConfig2" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties|policy_and_key\.hpp|AccountConfig\.h)$|hubot''' regex = '''(?i)(?:User ID|uid|UserId).*(?:Password|[^a-z]pwd)=[^'\$%<@'";\[\{][^;/"]{4,128}(?:;|")''' [rules.allowlist] @@ -88,44 +89,55 @@ regex = [ ] [[rules]] -id = "CSCAN0093 1" -description = "StorageAccountKeyInConfig 1" +id = "CSCAN0043" +description = "SqlConnectionStringInCode" +path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties|policy_and_key\.hpp|AccountConfig\.h)$|hubot''' +regex = '''(?i)(?:User ID|uid|UserId).*(?:Password|[^a-z]pwd)=[^'\$%<@'";\[\{][^;/"]{4,128}(?:;|")''' +[rules.allowlist] +regex = [ + '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', + '''(?:prefix <<|guestaccesstoken|skiptoken|cookie|tsm|fake|example|badlyFormatted|Invalid|sha512|sha256|"input"|ENCRYPTED|"EncodedRequestUri"|looks like|myStorageAccountName|(?:0|x|\*){8,})''', +] + +[[rules]] +id = "CSCAN0093" +description = "StorageAccountKeyInConfig" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{86}==''' [[rules]] -id = "CSCAN0041 1" -description = "StorageAccountKeyInCode 1" +id = "CSCAN0041" +description = "StorageAccountKeyInCode" path = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{86}==''' [[rules]] -id = "CSCAN0094 1" -description = "SharedAccessSignatureInCode 1" +id = "CSCAN0094-1" +description = "SharedAccessSignatureInCode1" path = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{43}=[^{@]''' [[rules]] -id = "CSCAN0094 2" -description = "SharedAccessSignatureInCode 2" +id = "CSCAN0094-2" +description = "SharedAccessSignatureInCode2" path = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9%]{43,53}%3d[^a-z0-9%]''' [[rules]] -id = "CSCAN0094 1" -description = "SharedAccessSignatureInConfig 1" +id = "CSCAN0094-3" +description = "SharedAccessSignatureInConfig1" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{43}=[^{@]''' [[rules]] -id = "CSCAN0094 2" -description = "SharedAccessSignatureInConfig 2" +id = "CSCAN0094-4" +description = "SharedAccessSignatureInConfig2" path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9%]{43,53}%3d[^a-z0-9%]''' [[rules]] -id = "CSCAN0095 1" -description = "GeneralSecretInConfig 1" +id = "CSCAN0095-1" +description = "GeneralSecretInConfig1" path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = ''']*/>''' [rules.allowlist] @@ -139,8 +151,8 @@ regex = [ ] [[rules]] -id = "CSCAN0095 2" -description = "GeneralSecretInConfig 2" +id = "CSCAN0095-2" +description = "GeneralSecretInConfig2" path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''''' [rules.allowlist] @@ -154,8 +166,8 @@ regex = [ ] [[rules]] -id = "CSCAN0095 3" -description = "GeneralSecretInConfig 3" +id = "CSCAN0095-3" +description = "GeneralSecretInConfig3" path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''|[^>]*>.*?)''' [rules.allowlist] @@ -169,8 +181,8 @@ regex = [ ] [[rules]] -id = "CSCAN0095 4" -description = "GeneralSecretInConfig 4" +id = "CSCAN0095-4" +description = "GeneralSecretInConfig4" path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' regex = '''.+''' [rules.allowlist] @@ -184,14 +196,14 @@ regex = [ ] [[rules]] -id = "CSCAN0110 1" -description = "ScriptPassword 1" +id = "CSCAN0110-1" +description = "ScriptPassword1" path = '''(?:\.cmd|\.ps|\.ps1|\.psm1)$''' regex = '''\s-Password\s+(?:"[^"]*"|'[^']*')''' [[rules]] -id = "CSCAN0110 2" -description = "ScriptPassword 2" +id = "CSCAN0110-2" +description = "ScriptPassword2" path = '''(?:\.cmd|\.ps|\.ps1|\.psm1)$''' regex = '''\s-Password\s+[^$\(\)\[\{<\-\r?\n]+\s*(?:\r?\n|\-)''' @@ -202,8 +214,8 @@ path = '''\.cs$|\.cpp$|\.c$''' regex = '''(private\sconst\sstring\sAccessTokenSecret|private\sconst\sstring\saccessToken|private\sconst\sstring\sconsumerSecret|private\sconst\sstring\sconsumerKey|pageAccessToken|private\sstring\stwilioAccountSid|private\sstring\stwilioAuthToken)\s=\s".*";''' [[rules]] -id = "CSCAN0220 1" -description = "DefaultPasswordContexts 1" +id = "CSCAN0220-1" +description = "DefaultPasswordContexts1" path = '''\.(?:ps1|psm1|)$''' regex = '''ConvertTo-SecureString(?:\s*-String)?\s*"[^$"\r?\n]+"''' [rules.allowlist] @@ -213,8 +225,8 @@ regex = [ ] [[rules]] -id = "CSCAN0220 2" -description = "DefaultPasswordContexts 2" +id = "CSCAN0220-2" +description = "DefaultPasswordContexts2" path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' regex = '''new\sX509Certificate2\([^()]*,\s*"[^"\r?\n]+"[^)]*\)''' [rules.allowlist] @@ -224,8 +236,8 @@ regex = [ ] [[rules]] -id = "CSCAN0220 3" -description = "DefaultPasswordContexts 3" +id = "CSCAN0220-3" +description = "DefaultPasswordContexts3" path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' regex = '''AdminPassword\s*=\s*"[^"\r?\n]+"''' [rules.allowlist] @@ -235,8 +247,8 @@ regex = [ ] [[rules]] -id = "CSCAN0220 4" -description = "DefaultPasswordContexts 4" +id = "CSCAN0220-4" +description = "DefaultPasswordContexts4" path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' regex = '''(?i).+''' [rules.allowlist] @@ -246,8 +258,8 @@ regex = [ ] [[rules]] -id = "CSCAN0220 5" -description = "DefaultPasswordContexts 5" +id = "CSCAN0220-5" +description = "DefaultPasswordContexts5" path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' regex = '''ClearTextPassword"?\s*[:=]\s*"[^"\r?\n]+"''' [rules.allowlist] @@ -257,8 +269,8 @@ regex = [ ] [[rules]] -id = "CSCAN0220 6" -description = "DefaultPasswordContexts 6" +id = "CSCAN0220-6" +description = "DefaultPasswordContexts6" path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' regex = '''certutil.*?\-p\s+("[^"%]+"|'[^'%]+'|[^"']\S*\s)''' [rules.allowlist] @@ -268,8 +280,8 @@ regex = [ ] [[rules]] -id = "CSCAN0220 7" -description = "DefaultPasswordContexts 7" +id = "CSCAN0220-7" +description = "DefaultPasswordContexts7" path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' regex = '''password\s*=\s*N?(["][^"\r?\n]{4,}["]|['][^'\r?\n]{4,}['])''' [rules.allowlist] @@ -288,26 +300,26 @@ regex = '''(%1%|\$MIGUSER_PASSWORD|%miguser_pwd%)''' description = "ignore placeholders" [[rules]] -id = "CSCAN0240 1" -description = "VstsPersonalAccessToken 1" +id = "CSCAN0240-1" +description = "VstsPersonalAccessToken1" path = '''\.(?:cs|ps1|bat|config|xml|json|md|yml|yaml)$''' regex = '''(?i)(?:AccessToken|pat|token).*?[':="][a-z0-9]{52}(?:'|"|\s|[\r?\n]+)''' [[rules]] -d = "CSCAN0240 2" -description = "VstsPersonalAccessToken 2" +d = "CSCAN0240-2" +description = "VstsPersonalAccessToken2" path = '''\.(?:cs|ps1|bat|config|xml|json|md|yml|yaml)$''' regex = '''(?i)(?:AccessToken|pat|token).*?[':="][a-z0-9/+]{70}==(?:'|"|\s|[\r?\n]+)''' [[rules]] -id = "CSCAN0250 1" -description = "OAuthToken 1" +id = "CSCAN0250-1" +description = "OauthToken1" path = '''\.(?:config|js|json|txt|cs|xml|java|py)$''' regex = '''eyj[a-z0-9\-_%]+\.eyj[a-z0-9\-_%]+\.[a-z0-9\-_%]+''' [[rules]] -id = "CSCAN0250 2" -description = "OAuthToken 2" +id = "CSCAN0250-2" +description = "OauthToken2" path = '''\.(?:config|js|json|txt|cs|xml|java|py)$''' regex = '''refresh_token["']?\s*[:=]\s*["']?(?:[a-z0-9_]+-)+[a-z0-9_]+["']?''' @@ -318,14 +330,14 @@ path = '''\.yml$''' regex = '''\$ANSIBLE_VAULT;[0-9]\.[0-9];AES256[\r?\n]+[0-9]+''' [[rules]] -id = "CSCAN0230 1" -description = "SlackToken 1" +id = "CSCAN0230-1" +description = "SlackToken1" regex = '''xoxp-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+''' path = '''\.(?:ps1|psm1|js|json|coffee|xml|js|md|html|py|php|java|ipynb|rb)$|hubot''' [[rules]] -id = "CSCAN0230 2" -description = "SlackToken 2" +id = "CSCAN0230-2" +description = "SlackToken2" regex = '''xoxb-[a-z0-9]+-[a-z0-9]+''' path = '''\.(?:ps1|psm1|js|json|coffee|xml|js|md|html|py|php|java|ipynb|rb)$|hubot''' @@ -337,6 +349,9 @@ paths = [ '''UDMSecretChecks.toml''', '''UDMSecretChecksv8.toml''', '''GitleaksUdmCombo.toml''', + '''.github/linters''', + '''node_modules''', + '''(.*?)gitleaks\.toml$''', ] commits = [] repos = [] diff --git a/LICENSE b/LICENSE index d7acf24..6cbcf89 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Dariusz Porowski +Copyright (c) 2022-2023 Dariusz Porowski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 63be94e..ce6aa80 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,28 @@ # GitHub Action for Gitleaks -[![GitHub release (latest by date)](https://img.shields.io/github/v/release/DariuszPorowski/github-action-gitleaks)](https://github.com/DariuszPorowski/github-action-gitleaks/releases) +[![GitHub - marketplace](https://img.shields.io/badge/marketplace-gitleaks--scanner-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/gitleaks-scanner) +[![GitHub - release](https://img.shields.io/github/v/release/DariuszPorowski/github-action-gitleaks?style=flat-square)](https://github.com/DariuszPorowski/github-action-gitleaks/releases/latest) +[![GitHub - license](https://img.shields.io/github/license/DariuszPorowski/github-action-gitleaks?style=flat-square)](https://github.com/DariuszPorowski/github-action-gitleaks/blob/main/LICENSE) This GitHub Action allows you to run [Gitleaks](https://github.com/gitleaks/gitleaks) in your CI/CD workflow. -> NOTE: v2 of this GitHub Action supports only the latest version of Gitleaks from v8 release. +> ⚠️ `v2` of this GitHub Action supports only the latest version of Gitleaks from v8 release. ## Inputs -| Name | Required | Type | Default value | Description | -|---------------|----------|--------|---------------------------------|----------------------------------------------------------------------------------| -| source | false | string | $GITHUB_WORKSPACE | Path to source (relative to $GITHUB_WORKSPACE) | -| config | false | string | /.gitleaks/UDMSecretChecks.toml | Config file path (relative to $GITHUB_WORKSPACE) | -| baseline_path | false | string | *not set* | Path to baseline with issues that can be ignored (relative to $GITHUB_WORKSPACE) | -| report_format | false | string | json | Report file format: json, csv, sarif | -| no_git | false | bool | *not set* | Treat git repos as plain directories and scan those file | -| redact | false | bool | true | Redact secrets from log messages and leaks | -| fail | false | bool | true | Fail if secrets founded | -| verbose | false | bool | true | Show verbose output from scan | -| log_level | false | string | info | Log level (trace, debug, info, warn, error, fatal) | +| Name | Required | Type | Default value | Description | +|---------------|:--------:|:------:|---------------------------------|----------------------------------------------------------------------------------| +| source | false | string | $GITHUB_WORKSPACE | Path to source (relative to $GITHUB_WORKSPACE) | +| config | false | string | /.gitleaks/UDMSecretChecks.toml | Config file path (relative to $GITHUB_WORKSPACE) | +| baseline_path | false | string | *not set* | Path to baseline with issues that can be ignored (relative to $GITHUB_WORKSPACE) | +| report_format | false | string | json | Report file format: json, csv, sarif | +| no_git | false | bool | *not set* | Treat git repos as plain directories and scan those file | +| redact | false | bool | true | Redact secrets from log messages and leaks | +| fail | false | bool | true | Fail if secrets founded | +| verbose | false | bool | true | Show verbose output from scan | +| log_level | false | string | info | Log level (trace, debug, info, warn, error, fatal) | -> __NOTE:__ The solution provides predefined configuration (See: [.gitleaks](https://github.com/DariuszPorowski/github-action-gitleaks/tree/main/.gitleaks) path). You can override it by yours config using relative to `$GITHUB_WORKSPACE`. +> ⚠️ The solution provides predefined configuration (See: [.gitleaks](https://github.com/DariuszPorowski/github-action-gitleaks/tree/main/.gitleaks) path). You can override it by yours config using relative to `$GITHUB_WORKSPACE`. ## Outputs @@ -34,10 +36,67 @@ This GitHub Action allows you to run [Gitleaks](https://github.com/gitleaks/gitl ## Example usage -> __NOTE:__ You must use actions/checkout before the `github-action-gitleaks` step. If you are using `actions/checkout@v3` you must specify a commit depth other than the default which is 1. +> ⚠️ You must use `actions/checkout` before the `github-action-gitleaks` step. If you are using `actions/checkout@v3` you must specify a commit depth other than the default which is 1. > > Using a `fetch-depth` of '0' clones the entire history. If you want to do a more efficient clone, use '2', but that is not guaranteed to work with pull requests. +### Pull Request with comment + +```yaml +--- +name: Secret Scan + +on: + pull_request: + push: + branches: + - main + +# allow one concurrency +concurrency: + group: ${{ format('{0}-{1}-{2}-{3}-{4}', github.workflow, github.event_name, github.ref, github.base_ref, github.head_ref) }} + cancel-in-progress: true + +jobs: + gitleaks: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Run Gitleaks + id: gitleaks + uses: DariuszPorowski/github-action-gitleaks@v2 + with: + fail: false + + - name: Post PR comment + uses: actions/github-script@v6 + if: ${{ steps.gitleaks.outputs.exitcode == 1 && github.event_name == 'pull_request' }} + with: + github-token: ${{ github.token }} + script: | + const { GITLEAKS_RESULT, GITLEAKS_OUTPUT } = process.env + const output = `### ${GITLEAKS_RESULT} + +
Log output + + ${GITLEAKS_OUTPUT} + +
+ ` + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body: output + }) + env: + GITLEAKS_RESULT: ${{ steps.gitleaks.outputs.result }} + GITLEAKS_OUTPUT: ${{ steps.gitleaks.outputs.output }} +``` + ### With SARIF report ```yaml @@ -54,13 +113,16 @@ This GitHub Action allows you to run [Gitleaks](https://github.com/gitleaks/gitl fail: false # (optional) It's just to see outputs from the Action +# please note, the OUTPUT has to be passed via env vars! - name: Get the output from the gitleaks step run: | echo "exitcode: ${{ steps.gitleaks.outputs.exitcode }}" echo "result: ${{ steps.gitleaks.outputs.result }}" - echo "output: ${{ steps.gitleaks.outputs.output }}" echo "command: ${{ steps.gitleaks.outputs.command }}" echo "report: ${{ steps.gitleaks.outputs.report }}" + echo "output: ${GITLEAKS_OUTPUT}" + env: + GITLEAKS_OUTPUT: ${{ steps.gitleaks.outputs.output }} - name: Upload Gitleaks SARIF report to code scanning service if: ${{ steps.gitleaks.outputs.exitcode == 1 }} @@ -69,7 +131,7 @@ This GitHub Action allows you to run [Gitleaks](https://github.com/gitleaks/gitl sarif_file: ${{ steps.gitleaks.outputs.report }} ``` -> __NOTE:__ SARIF file uploads for code scanning is not available for everyone. Read GitHub docs ([Uploading a SARIF file to GitHub](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github)) for more information. +> ⚠️ SARIF file uploads for code scanning is not available for everyone. Read GitHub docs ([Uploading a SARIF file to GitHub](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github)) for more information. ### With JSON report and custom rules config diff --git a/entrypoint.sh b/entrypoint.sh index 92f1ede..4eda4df 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -77,7 +77,7 @@ if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then base_sha=$(git rev-parse "refs/remotes/origin/${GITHUB_BASE_REF}") head_sha=$(git rev-list --no-merges -n 1 refs/remotes/pull/${GITHUB_REF_NAME}) - command+=$(arg '--log-opts "%s"' "--no-merges --first-parent ${base_sha}...${head_sha}") + command+=$(arg '--log-opts "%s"' "--no-merges --first-parent ${base_sha}^..${head_sha}") else command+=$(arg '--source %s' "${INPUT_SOURCE}") command+=$(arg '--no-git' "${INPUT_NO_GIT}") @@ -91,23 +91,26 @@ OUTPUT=$(eval "${command}") exitcode=$? if [ ${exitcode} -eq 0 ]; then - GITLEAKS_RESULT="SUCCESS! Your code is good to go" + GITLEAKS_RESULT="✅ SUCCESS! Your code is good to go" elif [ ${exitcode} -eq 1 ]; then - GITLEAKS_RESULT="STOP! Gitleaks encountered leaks or error" + GITLEAKS_RESULT="❌ STOP! Gitleaks encountered leaks or error" else GITLEAKS_RESULT="Gitleaks unknown error" fi echo "----------------------------------" -echo "${OUTPUT}" -echo "output=${OUTPUT}" >>$GITHUB_OUTPUT -echo "report=gitleaks-report.${INPUT_REPORT_FORMAT}" >>$GITHUB_OUTPUT -echo "result=${GITLEAKS_RESULT}" >>$GITHUB_OUTPUT -echo "command=${command}" >>$GITHUB_OUTPUT -echo "exitcode=${exitcode}" >>$GITHUB_OUTPUT - -echo "Gitleaks Summary: ${GITLEAKS_RESULT}" >>$GITHUB_STEP_SUMMARY -echo "${OUTPUT}" >>$GITHUB_STEP_SUMMARY +echo -e "${OUTPUT}" + +EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) +echo "output<<$EOF" >>"$GITHUB_OUTPUT" +echo -e "${OUTPUT}" >>"$GITHUB_OUTPUT" +echo "$EOF" >>"$GITHUB_OUTPUT" +echo "report=gitleaks-report.${INPUT_REPORT_FORMAT}" >>"$GITHUB_OUTPUT" +echo "result=${GITLEAKS_RESULT}" >>"$GITHUB_OUTPUT" +echo "command=${command}" >>"$GITHUB_OUTPUT" +echo "exitcode=${exitcode}" >>"$GITHUB_OUTPUT" +echo -e "Gitleaks Summary: ${GITLEAKS_RESULT}\n" >>"$GITHUB_STEP_SUMMARY" +echo -e "${OUTPUT}" >>"$GITHUB_STEP_SUMMARY" if [ ${exitcode} -eq 0 ]; then echo "::notice::${GITLEAKS_RESULT}"