diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..63f4e356 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,16 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. + +# More details are here: https://help.github.com/articles/about-codeowners/ + +# The '*' pattern is global owners. + +# Order is important. The last matching pattern has the most precedence. +# The folders are ordered as follows: + +# In each subsection folders are ordered first by depth, then alphabetically. +# This should make it easy to add new rules without breaking existing ones. + +# The following GitHub teams can be used within this file: +# @AI21/devops +# @AI21/writing diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d2f790dc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json + +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + commit-message: + prefix: chore(deps) diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 00000000..c4492a33 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,31 @@ +{ + "extends": [ + "config:base" + ], + "dependencyDashboard": true, + "semanticCommits": "enabled", + "labels": [ + "dependencies" + ], + "regexManagers": [ + { + "fileMatch": [ + "(^|/)\\.pre-commit-config\\.yaml$" + ], + "matchStrings": [ + "\\nminimum_pre_commit_version: (?.*?)\\n" + ], + "depNameTemplate": "pre-commit", + "datasourceTemplate": "pypi" + }, + { + "fileMatch": [ + "(^|/)\\.pre-commit-config\\.yaml$" + ], + "matchStrings": [ + "\\n\\s*entry: (?[^:]+):(?\\S+)" + ], + "datasourceTemplate": "docker" + } + ] +} diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 00000000..941b2da4 --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,158 @@ +# These settings are synced to GitHub by https://probot.github.io/apps/settings/ + +repository: + # See https://docs.github.com/en/rest/reference/repos#update-a-repository for all available settings. + + # The name of the repository. Changing this will rename the repository. + # name: template + + # A short description of the repository that will show up on GitHub. + description: GitHub Template Repository + + # A URL with more information about the repository + # homepage: https://example.github.io/ + + # A comma-separated list of topics to set on the repository + topics: template + + # Either `true` to enable issues for this repository, `false` to disable them. + has_issues: true + + # Either `true` to enable projects for this repository, or `false` to disable them. + # If projects are disabled for the organization, passing `true` will cause an API error. + has_projects: false + + # Either `true` to enable the wiki for this repository, `false` to disable it. + has_wiki: false + + # Either `true` to enable downloads for this repository, `false` to disable them. + has_downloads: false + + # Updates the default branch for this repository. + default_branch: main + + # Either `true` to allow squash-merging pull requests, or `false` to prevent + # squash-merging. + allow_squash_merge: true + + # Either `true` to allow merging pull requests with a merge commit, or `false` + # to prevent merging pull requests with merge commits. + allow_merge_commit: true + + # Either `true` to allow rebase-merging pull requests, or `false` to prevent + # rebase-merging. + allow_rebase_merge: true + + # Either `true` to allow auto-merge on pull requests, or `false` to disallow auto-merge + allow_auto_merge: true + + # Either `true` to enable automatic deletion of branches on merge, or `false` to disable + delete_branch_on_merge: true + + # Either `true` to enable automated security fixes, or `false` to disable + # automated security fixes. + enable_automated_security_fixes: true + + # Either `true` to enable vulnerability alerts, or `false` to disable + # vulnerability alerts. + enable_vulnerability_alerts: true + + # Either `true` to make this repo available as a template repository or `false` to prevent it. + # is_template: false + # template_repository: false + + # `true` to archive this repository. Note: You cannot unarchive repositories through the API. + archived: false + +# Labels: define labels for Issues and Pull Requests +labels: + - name: bug + color: '#cc0000' + description: An issue with the system 🐛. + + - name: feature + # If including a `#`, make sure to wrap it with quotes! + color: '#336699' + description: New functionality + + - name: WIP + # If including a `#`, make sure to wrap it with quotes! + color: '#faf064' + description: Work in progress + + - name: draft + # If including a `#`, make sure to wrap it with quotes! + color: '#c800c8' + +# Milestones: define milestones for Issues and Pull Requests +# milestones: +# - title: milestone-title +# description: milestone-description +# # The state of the milestone. Either `open` or `closed` +# state: open + +# Collaborators: give specific users access to this repository. +# See https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator for available options +# collaborators: +# - username: my-user +# # Note: `permission` is only valid on organization-owned repositories. +# # The permission to grant the collaborator. Can be one of: +# # * `pull` - can pull, but not push to or administer this repository. +# # * `push` - can pull and push, but not administer this repository. +# # * `admin` - can pull, push and administer this repository. +# # * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions. +# # * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access. +# permission: admin + +# See https://developer.github.com/v3/teams/#add-or-update-team-repository for available options +teams: + - name: rnd + # The permission to grant the team. Can be one of: + # * `pull` - can pull, but not push to or administer this repository. + # * `push` - can pull and push, but not administer this repository. + # * `admin` - can pull, push and administer this repository. + # * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions. + # * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access. + permission: push + +branches: + - name: main + # https://docs.github.com/en/rest/reference/repos#update-branch-protection + # Branch Protection settings. Set to null to disable + protection: + # Required. Require at least one approving review on a pull request, before merging. Set to null to disable. + required_pull_request_reviews: + # The number of approvals required. (1-6) + required_approving_review_count: 1 + # Dismiss approved reviews automatically when a new commit is pushed. + dismiss_stale_reviews: true + # Blocks merge until code owners have reviewed. + require_code_owner_reviews: true + # Specify which users and teams can dismiss pull request reviews. Pass an empty dismissal_restrictions object to disable. User and team dismissal_restrictions are only available for organization-owned repositories. Omit this parameter for personal repositories. + dismissal_restrictions: {} + # users: [] + # teams: [] + # Required. Require status checks to pass before merging. Set to null to disable + required_status_checks: + # Required. Require branches to be up to date before merging. + strict: true + # Required. The list of status checks to require in order to merge into this branch + contexts: + - quality-checks + # Commits pushed to matching branches must have verified signatures. Set to false to disable. + required_signatures: false + # Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable. + enforce_admins: true + # Prevents merge commits from being pushed to matching branches. Set to false to disable. + required_linear_history: true + # Required. Restrict who can push to this branch. Team and user restrictions are only available for organization-owned repositories. Set to null to disable. + restrictions: + # apps: [] + # users: [] + # teams: [] + # Permits force pushes for all users with push access. Set to null to disable. + allow_force_pushes: + # Allows users with push access to delete matching branches. Set to false to disable. + allow_deletions: false + # Ensure all review conversations are seen and addressed prior to merging + required_conversation_resolution: true diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..e943a7fb --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,42 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 3 + +# Number of days of inactivity before an Issue or Pull Request with the stale +# label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, +# but will remain marked as stale. +daysUntilClose: 3 + +# Issues or Pull Requests with these labels will never be considered stale. +# Set to `[]` to disable +exemptLabels: + - dependencies + - draft + - WIP + +# Label to use when marking as stale +staleLabel: stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This PR has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +closeComment: > + This PR has been automatically closed because it has not had recent activity. + You can reopen it by clicking on `Reopen pull request`. + Thank you for your contributions. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +only: pulls diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml new file mode 100644 index 00000000..ba62006a --- /dev/null +++ b/.github/workflows/quality-checks.yml @@ -0,0 +1,27 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Quality Checks +concurrency: + group: Quality-Checks-${{ github.head_ref }} + cancel-in-progress: true +on: + pull_request: +jobs: + quality-checks: + runs-on: ubuntu-20.04 + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v3.2.0 + with: + fetch-depth: 0 + - name: Pre-commit + uses: pre-commit/action@v3.0.0 + with: + extra_args: --from-ref ${{ github.event.pull_request.base.sha }} --to-ref ${{ github.event.pull_request.head.sha }} + # - name: CODEOWNERS validator + # uses: mszostok/codeowners-validator@v0.6.0 + # with: + # checks: files,duppatterns,syntax,owners + # experimental_checks: notowned + # github_access_token: ${{ secrets.GH_PAT_RO }} diff --git a/.github/workflows/semantic-pr.yml b/.github/workflows/semantic-pr.yml new file mode 100644 index 00000000..955b7473 --- /dev/null +++ b/.github/workflows/semantic-pr.yml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Semantic PR +concurrency: + group: Semantic-PR-${{ github.head_ref }} + cancel-in-progress: true +on: + pull_request: + types: + - opened + - edited + - reopened +jobs: + semantic-pr: + runs-on: ubuntu-20.04 + timeout-minutes: 1 + steps: + - name: Semantic pull-request + uses: amannn/action-semantic-pull-request@v5.0.2 + with: + requireScope: false + wip: true + validateSingleCommit: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..307a93bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,120 @@ +# https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# https://github.com/github/gitignore/blob/master/Global/Archives.gitignore +# It's better to unpack these files and commit the raw source because +# git has its own built in compression methods. +*.7z +*.jar +*.rar +*.zip +*.gz +*.gzip +*.tgz +*.bzip +*.bzip2 +*.bz2 +*.xz +*.lzma +*.cab +*.xar + +# Packing-only formats +*.iso +*.tar + +# Package management formats +*.dmg +*.xpi +*.gem +*.egg +*.deb +*.rpm +*.msi +*.msm +*.msp +*.txz + +# https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# IntelliJ +.idea/ +out/ + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# File-based project format +*.iws + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# https://github.com/github/gitignore/blob/master/Global/Backup.gitignore +*.bak +*.gho +*.ori +*.orig +*.tmp + +# https://github.com/github/gitignore/blob/master/Global/Diff.gitignore +*.patch +*.diff + +# https://github.com/github/gitignore/blob/master/Global/Patch.gitignore +*.orig +*.rej diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..85c050bd --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,590 @@ +# ported from https://git.io/J4rOB @ 2021-08-31T09:20:42+0300 + +title = "gitleaks config" + +[[rules]] + description = "AWS Manager ID" + regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' + tags = ["key", "AWS"] + [rules.allowlist] + regexes = ['''(arn:aws:)'''] + description = "ignore arns" + +[[rules]] + description = "AWS Secret Key" + regex = '''(?i)aws(.{0,20})?(?-i)['\"][0-9a-zA-Z\/+]{40}['\"]''' + tags = ["key", "AWS"] + +[[rules]] + description = "AWS MWS key" + regex = '''amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''' + tags = ["key", "AWS", "MWS"] + +[[rules]] + description = "Facebook Secret Key" + regex = '''(?i)(facebook|fb)(.{0,20})?(?-i)['\"][0-9a-f]{32}['\"]''' + tags = ["key", "Facebook"] + +[[rules]] + description = "Facebook Client ID" + regex = '''(?i)(facebook|fb)(.{0,20})?['\"][0-9]{13,17}['\"]''' + tags = ["key", "Facebook"] + +[[rules]] + description = "Twitter Secret Key" + regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{35,44}['\"]''' + tags = ["key", "Twitter"] + +[[rules]] + description = "Twitter Client ID" + regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{18,25}['\"]''' + tags = ["client", "Twitter"] + [rules.allowlist] + regexes = ['''from [\\w_\\.]+ import [\\w_\\.]+'''] + description = "Ignore python imports" + +[[rules]] + description = "Github" + regex = '''(?i)github.{0,3}((?i)token|api|key).{0,10}?(?-i)([0-9a-zA-Z]{35,40})''' + tags = ["key", "Github"] + +[[rules]] + description = "LinkedIn Client ID" + regex = '''(?i)linkedin(.{0,20})?(?-i)['\"][0-9a-z]{12}['\"]''' + tags = ["client", "LinkedIn"] + +[[rules]] + description = "LinkedIn Secret Key" + regex = '''(?i)linkedin(.{0,20})?['\"][0-9a-z]{16}['\"]''' + tags = ["secret", "LinkedIn"] + +[[rules]] + description = "NPM Token" + regex= '''//registry.npmjs.org/:_authToken=[0-9a-f-]+''' + tags = ["key", "NPM"] + +[[rules]] + description = "Slack" + regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?''' + tags = ["key", "Slack"] + +[[rules]] + description = "EC" + regex = '''-----BEGIN EC PRIVATE KEY-----''' + tags = ["key", "EC"] + +[[rules]] + description = "DSA" + regex = '''-----BEGIN DSA PRIVATE KEY-----''' + tags = ["key", "DSA"] + +[[rules]] + description = "OPENSSH" + regex = '''-----BEGIN OPENSSH PRIVATE KEY-----''' + tags = ["key", "OPENSSH"] + +[[rules]] + description = "RSA" + regex = '''-----BEGIN RSA PRIVATE KEY-----''' + tags = ["key", "RSA"] + +[[rules]] + description = "PGP" + regex = '''-----BEGIN PGP PRIVATE KEY BLOCK-----''' + tags = ["key", "PGP"] + +[[rules]] + description = "Google API key" + regex = '''AIza[0-9A-Za-z\\-_]{35}''' + tags = ["key", "Google"] + + +[[rules]] + description = "Heroku API key" + regex = '''(?i)heroku(.{0,20})?['"][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}['"]''' + tags = ["key", "Heroku"] + +[[rules]] + description = "MailChimp API key" + regex = '''(?i)(mailchimp|mc)(.{0,20})?['"][0-9a-f]{32}-us[0-9]{1,2}['"]''' + tags = ["key", "Mailchimp"] + +[[rules]] + description = "Mailgun API key" + regex = '''(?i)(mailgun|mg)(.{0,20})?['"][0-9a-z]{32}['"]''' + tags = ["key", "Mailgun"] + +[[rules]] + description = "PayPal Braintree access token" + regex = '''access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}''' + tags = ["key", "Paypal"] + +[[rules]] + description = "Picatic API key" + regex = '''sk_live_[0-9a-z]{32}''' + tags = ["key", "Picatic"] + +[[rules]] + description = "Slack Webhook" + regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}''' + tags = ["key", "slack"] + +[[rules]] + description = "Stripe API key" + regex = '''(?i)stripe(.{0,20})?['\"][sk|rk]_live_[0-9a-zA-Z]{24}''' + tags = ["key", "Stripe"] + +[[rules]] + description = "Square access token" + regex = '''sq0atp-[0-9A-Za-z\-_]{22}''' + tags = ["key", "square"] + +[[rules]] + description = "Square OAuth secret" + regex = '''sq0csp-[0-9A-Za-z\\-_]{43}''' + tags = ["key", "square"] + +[[rules]] + description = "Twilio API key" + regex = '''(?i)twilio(.{0,20})?['\"][0-9a-f]{32}['\"]''' + tags = ["key", "twilio"] + +[[rules]] + description = "Dynatrace token" + regex = '''dt0[a-zA-Z]{1}[0-9]{2}\.[A-Z0-9]{24}\.[A-Z0-9]{64}''' + tags = ["key", "Dynatrace"] + +[[rules]] + description = "Shopify shared secret" + regex = '''shpss_[a-fA-F0-9]{32}''' + tags = ["key", "Shopify"] + +[[rules]] + description = "Shopify access token" + regex = '''shpat_[a-fA-F0-9]{32}''' + tags = ["key", "Shopify"] + +[[rules]] + description = "Shopify custom app access token" + regex = '''shpca_[a-fA-F0-9]{32}''' + tags = ["key", "Shopify"] + +[[rules]] + description = "Shopify private app access token" + regex = '''shppa_[a-fA-F0-9]{32}''' + tags = ["key", "Shopify"] + +[[rules]] + description = "Env Var" + regex = '''(?i)(apikey|secret|password|certificate_osx_p12|certificate_password|codacy_project_token|coveralls_api_token|coveralls_repo_token|coveralls_token|coverity_scan_token|cypress_record_key|database_password|db_password|deploy_password|deploy_token|digitalocean_access_token|docker_hub_password|docker_password|dockerhub_password|sonatype_password|firebase_api_token|firebase_token|firefox_secret|flask_secret_key|fossa_api_key|gcloud_service_key|gcr_password|gh_api_key|gh_next_oauth_client_secret|gh_next_unstable_oauth_client_secret|gh_oauth_client_secret|gpg_private_key|gpg_passphrase|gpg_secret_keys|heroku_api_key|okta_client_token|pypi_password|sonatype_nexus_password|travis_token|refresh_token|client_id|client_secret)(.*)?[(:=](\s)?['\"][0-9a-zA-Z-_!$^%=]{10,120}['\")]''' + tags = ["key", "env"] + [[rules.entropies]] + Min = "4.2" + Max = "7.0" + [rules.allowlist] + regexes = ['''(?i)(expect|assert|Component|Control|passwd|attrgetter|itemgetter|ifloordiv|imatmul|itruediv|yourpassword|foo|bar|serial|mpan|secret_key|grant_type|ngModel)'''] + description = "ignore default" + +[[rules]] + description = "Static key" + regex = '''(?i)(cookieParser)(.*)?[(](\s)?['\"][0-9a-zA-Z-_!$^%=]{5,20}['\")]''' + tags = ["key", "sign"] + file = '''\.(js|ts)$''' + [rules.allowlist] + regexes = ['''(?i)(require|import|expect|assert|Component|Control|passwd|attrgetter|itemgetter|ifloordiv|imatmul|itruediv|yourpassword|foo|bar|serial|mpan|secret_key)'''] + description = "ignore default" + +[[rules]] + description = "High Entropy" + regex = '''[0-9a-zA-Z-_!=]{10,120}''' + tags = ["entropy"] + [[rules.entropies]] + Min = "4.8" + Max = "7.0" + [rules.allowlist] + description = "ignore ssh key and pems" + regexes = ['''(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789|abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890|0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz)'''] + paths = ['''(.*)?(ssh|i18n|locales)'''] + +[[rules]] + description = "WP-Config" + regex='''define(.{0,20})?(DB_CHARSET|NONCE_SALT|LOGGED_IN_SALT|AUTH_SALT|NONCE_KEY|DB_HOST|DB_PASSWORD|AUTH_KEY|SECURE_AUTH_KEY|LOGGED_IN_KEY|DB_NAME|DB_USER)(.{0,20})?['|"].{10,120}['|"]''' + tags = ["key", "API", "generic"] + +[[rules]] + description = "ShiftLeft token" + regex = '''.''' + file = '''shiftleft\.json''' + tags = ["file", "ShiftLeft"] + +[[rules]] + description = "Shell history" + regex = '''.''' + file = '''\.?(bash_|zsh_|z|mysql_|psql_|irb_)?history$''' + tags = ["file", "Shell"] + +[[rules]] + description = "Postgres password" + regex = '''.''' + file = '''\.pgpass$''' + tags = ["file", "password"] + +[[rules]] + description = "OpenVPN" + regex = '''.''' + file = '''\.ovpn$''' + tags = ["file", "openvpn"] + +[[rules]] + description = "Unknown Key" + regex = '''.''' + file = '''\.key$''' + tags = ["file", "key"] + +[[rules]] + description = "Keychain file" + regex = '''.''' + file = '''\.(kdb|agilekeychain|keychain|kwallet|git-credentials|proftpdpasswd|dockercfg|credentials)$''' + tags = ["file", "keychain"] + +[[rules]] + description = "s3cfg" + regex = '''.''' + file = '''\.s3cfg$''' + tags = ["file", "s3cfg"] + +[[rules]] + description = "secret token" + regex = '''.''' + file = '''(omniauth|secret_token|carrierwave)\.rb$''' + tags = ["file", "Ruby"] + +[[rules]] + description = "CSCAN0210: GitCredential" + regex = '''https?://.+:.+@.*''' + file = '''\.gitCredentials$''' + tags = ["file", "gitCredentials"] + +[[rules]] + description = "CSCAN0010: KeyStoreFile" + regex = '''.''' + file = '''\.keystore$''' + tags = ["file", "KeyStoreFile"] + +[[rules]] + description = "CSCAN0020: Base64EncodedCertificateInCode" + regex = '''['">;=]MII[a-z0-9/+]{200}''' + file = '''\.(?:cs|ini|json|ps1|publishsettings|template|trd|ts|xml)$''' + tags = ["key", "Base64EncodedCertificateInCode"] + +[[rules]] + description = "CSCAN0020: Base64EncodedCertificateInFile" + regex = '''MII[A-Za-z0-9/+]{60}''' + file = '''\.(?:cert|cer)$''' + tags = ["key", "Base64EncodedCertificateInFile"] + +[[rules]] + description = "CSCAN0030: PublishSettings" + regex = '''userPWD="[a-zA-Z0-9\+\/]{60}"''' + file = '''(?i)(publishsettings|\.pubxml$)''' + tags = ["key", "PublishSettings"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0060: PemFile 1" + file = '''\.pem$''' + regex = '''-{5}BEGIN(?: (?:[dr]sa|ec|openssh))? PRIVATE KEY-{5}''' + tags = ["key", "PemFile"] + +[[rules]] + description = "CSCAN0091: AspNetMachineKeyInConfig 1" + file = '''\.(?: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,})[^>]+>''' + tags = ["key", "AspNetMachineKeyInConfig"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0091: AspNetMachineKeyInConfig 2" + file = '''\.(?: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]+"''' + tags = ["key", "AspNetMachineKeyInConfig"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0092: SqlConnectionStringInConfig 1" + file = '''\.(?: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;][^"']*(?:'|")''' + tags = ["key", "SqlConnectionStringInConfig"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + +[[rules]] + description = "CSCAN0092: SqlConnectionStringInConfig 2 / CSCAN0043 SqlConnectionStringInCode" + file = '''\.(?: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}(?:;|")''' + tags = ["key", "SqlConnectionStringInConfig"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:prefix <<|guestaccesstoken|skiptoken|cookie|tsm|fake|example|badlyFormatted|Invalid|sha512|sha256|"input"|ENCRYPTED|"EncodedRequestUri"|looks like|myStorageAccountName|(?:0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0093: StorageAccountKeyInConfig 1" + file = '''\.(?: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}==''' + tags = ["key", "StorageAccountKeyInConfig"] + +[[rules]] + description = "CSCAN0041: StorageAccountKeyInCode 1" + file = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' + regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{86}==''' + tags = ["key", "StorageAccountKeyInCode"] + +[[rules]] + description = "CSCAN0094: SharedAccessSignatureInCode 1" + file = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' + regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{43}=[^{@]''' + tags = ["key", "SharedAccessSignatureInCode"] + +[[rules]] + description = "CSCAN0094: SharedAccessSignatureInCode 2" + file = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' + regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9%]{43,53}%3d[^a-z0-9%]''' + tags = ["key", "SharedAccessSignatureInCode"] + +[[rules]] + description = "CSCAN0094: SharedAccessSignatureInConfig 1" + file = '''\.(?: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}=[^{@]''' + tags = ["key", "SharedAccessSignatureInConfig"] + +[[rules]] + description = "CSCAN0094: SharedAccessSignatureInConfig 2" + file = '''\.(?: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%]''' + tags = ["key", "SharedAccessSignatureInConfig"] + +[[rules]] + description = "CSCAN0095: GeneralSecretInConfig 1" + file = '''\.(?: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 = ''']*/>''' + tags = ["key", "GeneralSecretInConfig"] + [[rules.whitelist]] + regex = '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''' + [[rules.whitelist]] + regex = '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''' + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + [[rules.whitelist]] + regex = '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''' + +[[rules]] + description = "CSCAN0095: GeneralSecretInConfig 2" + file = '''\.(?: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 = '''''' + tags = ["key", "GeneralSecretInConfig"] + [[rules.whitelist]] + regex = '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''' + [[rules.whitelist]] + regex = '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''' + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + [[rules.whitelist]] + regex = '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''' + +[[rules]] + description = "CSCAN0095: GeneralSecretInConfig 3" + file = '''\.(?: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 = '''|[^>]*>.*?)''' + tags = ["key", "GeneralSecretInConfig"] + [[rules.whitelist]] + regex = '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''' + [[rules.whitelist]] + regex = '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''' + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + [[rules.whitelist]] + regex = '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''' + +[[rules]] + description = "CSCAN0095: GeneralSecretInConfig 4" + file = '''\.(?: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 = '''.+''' + tags = ["key", "GeneralSecretInConfig"] + [[rules.whitelist]] + regex = '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''' + [[rules.whitelist]] + regex = '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''' + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + [[rules.whitelist]] + regex = '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''' + [[rules.whitelist]] + regex = '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''' + +[[rules]] + description = "CSCAN0110: ScriptPassword 1" + file = '''(?:\.cmd|\.ps|\.ps1|\.psm1)$''' + regex = '''\s-Password\s+(?:"[^"]*"|'[^']*')''' + tags = ["key", "ScriptPassword"] + +[[rules]] + description = "CSCAN0110: ScriptPassword 2" + file = '''(?:\.cmd|\.ps|\.ps1|\.psm1)$''' + regex = '''\s-Password\s+[^$\(\)\[\{<\-\r?\n]+\s*(?:\r?\n|\-)''' + tags = ["key", "ScriptPassword"] + +[[rules]] + description = "CSCAN0120: ExternalApiSecret" + file = '''\.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".*";''' + tags = ["key", "ExternalApiSecret"] + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 1" + file = '''\.(?:ps1|psm1|)$''' + regex = '''ConvertTo-SecureString(?:\s*-String)?\s*"[^"\r?\n]+"''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 2" + file = '''\.(?: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|argfile)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' + regex = '''new\sX509Certificate2\([^()]*,\s*"[^"\r?\n]+"[^)]*\)''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 3" + file = '''\.(?: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|argfile)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' + regex = '''AdminPassword\s*=\s*"[^"\r?\n]+"''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 4" + file = '''\.(?: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|argfile)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' + regex = '''(?i).+''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 5" + file = '''\.(?: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|argfile)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' + regex = '''ClearTextPassword"?\s*[:=]\s*"[^"\r?\n]+"''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 6" + file = '''\.(?: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|argfile)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' + regex = '''certutil.*?\-p\s+("[^"%]+"|'[^'%]+'|[^"']\S*\s)''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0220: DefaultPasswordContexts 7" + file = '''\.(?:cs|xml|config|json|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|argfile)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' + regex = '''password\s*=\s*N?(["][^"\r?\n]{4,}["]|['][^'\r?\n]{4,}['])''' + tags = ["key", "DefaultPasswordContexts"] + [[rules.whitelist]] + regex = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' + [[rules.whitelist]] + regex = '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''' + +[[rules]] + description = "CSCAN0160: DomainPassword" + regex = '''new(?:-object)?\s+System.Net.NetworkCredential\(?:.*?,\s*"[^"]+"''' + file = '''\.cs$|\.c$|\.cpp$|\.ps1$|\.ps$|\.cmd$|\.bat$|\.log$|\.psd$|\.psm1$''' + tags = ["key", "DomainPassword"] + [[rules.whitelist]] + regex = '''(%1%|\$MIGUSER_PASSWORD|%miguser_pwd%)''' + description = "ignore placeholders" + +[[rules]] + description = "CSCAN0240: VstsPersonalAccessToken 1" + file = '''\.(?:cs|ps1|bat|config|xml|json)$''' + regex = '''(?i)(?:AccessToken|pat|token).*?[':="][a-z0-9]{52}(?:'|"|\s|[\r?\n]+)''' + tags = ["key", "VstsPersonalAccessToken"] + +[[rules]] + description = "CSCAN0240: VstsPersonalAccessToken 1" + file = '''\.(?:cs|ps1|bat|config|xml|json)$''' + regex = '''(?i)(?:AccessToken|pat|token).*?[':="][a-z0-9/+]{70}==(?:'|"|\s|[\r?\n]+)''' + tags = ["key", "VstsPersonalAccessToken"] + +[[rules]] + description = "CSCAN0250: OAuthToken 1" + file = '''\.(?:config|js|json|txt|cs|xml|java|py)$''' + regex = '''eyj[a-z0-9\-_%]+\.eyj[a-z0-9\-_%]+\.[a-z0-9\-_%]+''' + tags = ["key", "OAuthToken"] + +[[rules]] + description = "CSCAN0250: OAuthToken 2" + file = '''\.(?:config|js|json|txt|cs|xml|java|py)$''' + regex = '''refresh_token["']?\s*[:=]\s*["']?(?:[a-z0-9_]+-)+[a-z0-9_]+["']?''' + tags = ["key", "OAuthToken"] + +[[rules]] + description = "CSCAN0260: AnsibleVault" + file = '''\.yml$''' + regex = '''\$ANSIBLE_VAULT;[0-9]\.[0-9];AES256[\r?\n]+[0-9]+''' + tags = ["key", "AnsibleVault"] + +[[rules]] + description = "CSCAN0230: SlackToken 1" + regex = '''xoxp-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+''' + file = '''\.(?:ps1|psm1|js|json|coffee|xml|js|md|html|py|php|java|ipynb|rb)$|hubot''' + tags = ["key", "SlackToken"] + +[[rules]] + description = "CSCAN0230: SlackToken 2" + regex = '''xoxb-[a-z0-9]+-[a-z0-9]+''' + file = '''\.(?:ps1|psm1|js|json|coffee|xml|js|md|html|py|php|java|ipynb|rb)$|hubot''' + tags = ["key", "SlackToken"] + +[allowlist] + description = "Allowlisted files" + files = ['''(.*?)(png|jpg|gif|doc|docx|pdf|bin|xls|zip)$''', '''buildsearchers.xml''','''(?i)(credscan-config.toml|go.mod|go.sum|yarn.lock|package-lock.json|.terraform.lock.hcl)'''] + paths = ['''(?i)(test|reports|node_modules|venv|.venv|.m2|.gradle)'''] diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 00000000..6f68d6c7 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://json.schemastore.org/markdownlint.json + +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml +# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md + +# Default state for all rules +default: true + +MD013: + line_length: 300 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..056cf5e8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,183 @@ +# yaml-language-server: $schema=https://json.schemastore.org/pre-commit-config.json + +minimum_pre_commit_version: 2.20.0 +fail_fast: false +default_stages: + - commit +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + - id: check-merge-conflict + - id: check-symlinks + - id: detect-private-key + exclude: .gitleaks.toml + - id: no-commit-to-branch + - id: trailing-whitespace + - id: end-of-file-fixer + - id: mixed-line-ending + - id: check-json + - id: check-toml + - id: check-xml + - id: pretty-format-json + args: + - --autofix + - --no-sort-keys + - repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 2.1.5 + hooks: + - id: forbid-binary + - id: git-check + # - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt + # rev: 0.1.0 + # hooks: + # - id: yamlfmt + # args: + # - --mapping + # - '2' + # - --sequence + # - '4' + # - --offset + # - '2' + # - --width + # - '300' + # - --implicit_start + - repo: https://github.com/adrienverge/yamllint + rev: v1.26.3 + hooks: + - id: yamllint + name: Lint YAML files + args: + - --strict + - repo: https://github.com/sirosen/check-jsonschema + rev: 0.4.1 + hooks: + - id: check-jsonschema + name: Validate GitHub Workflows + files: ^\.github/workflows/.*\.yml + types: + - yaml + args: + - --schemafile + - https://json.schemastore.org/github-workflow.json + - id: check-jsonschema + name: Validate GitHub Actions + files: > + (?x)^( + .*/action\.(yml|yaml)| + \.github/actions/.* + )$ + types: + - yaml + args: + - --schemafile + - https://json.schemastore.org/github-action + - id: check-jsonschema + name: Validate DependaBot + files: ^\.github/dependabot\.yml + types: + - yaml + args: + - --schemafile + - https://json.schemastore.org/dependabot-2.0.json + - id: check-jsonschema + name: Validate MarkdownLint + files: .*\.markdownlint\.yaml + types: + - yaml + args: + - --schemafile + - https://json.schemastore.org/markdownlint.json + - id: check-jsonschema + name: Validate YamlLint + files: .*\.yamllint\.yaml + types: + - yaml + args: + - --schemafile + - https://json.schemastore.org/yamllint.json + - id: check-jsonschema + name: Validate Pre-commit + files: .*\.pre-commit-config\.yaml + types: + - yaml + args: + - --schemafile + - https://json.schemastore.org/pre-commit-config.json + - id: check-jsonschema + name: Validate Docker-Compose + files: .*docker-compose\.yml + types: + - yaml + args: + - --schemafile + - https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json + - id: check-jsonschema + name: Validate Renovate + files: ^\.github/renovate\.json + types: + - json + args: + - --schemafile + - https://docs.renovatebot.com/renovate-schema.json + - repo: https://github.com/commitizen-tools/commitizen + rev: v2.18.0 + hooks: + - id: commitizen + name: Lint commit message + stages: + - commit-msg + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.7.2.1 + hooks: + - id: shellcheck + name: Check sh files (and patch) + entry: bash -eo pipefail -c 'shellcheck $@ -f diff | patch -p 1' -- + - id: shellcheck + name: Check sh files (and print violations) + - repo: local + hooks: + - id: list-files + name: List files + language: system + entry: bash -c 'echo $@' + stages: + - manual + - id: shfmt + name: Format sh files + language: docker_image + entry: mvdan/shfmt:v3.4.0 + args: + - -w + - -s + - -i + - '2' + types: + - shell + - id: markdownlint + name: Lint Markdown files + language: docker_image + entry: 06kellyjac/markdownlint-cli:0.28.1 + args: + - --fix + types: + - markdown + - id: hadolint + name: Lint Dockerfiles + language: docker_image + entry: hadolint/hadolint:v2.8.0 hadolint + types: + - dockerfile + - id: gitleaks + name: Detect hardcoded secrets + language: docker_image + entry: zricethezav/gitleaks:v7.6.1 + args: + - --append-repo-config + - --config-path + - .gitleaks.toml + - --verbose diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 00000000..db5dd90c --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://json.schemastore.org/yamllint.json + +extends: default + +rules: + line-length: + max: 300 + document-start: disable + truthy: + check-keys: false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..124ba5c9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 1970-01-01 00:00 UTC diff --git a/README.md b/README.md new file mode 100644 index 00000000..6387e54a --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Git Repository Template + +[Template](https://github.com/DynamicYield/template) is a [*Git repository template*](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) for bootstrapping your repositories. + +Bundled support for: + +- [Code Owners](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#about-code-owners) — Automatically requested for review when someone opens a pull request that modifies code that they own as defined in [`.github/CODEOWNERS`](.github/CODEOWNERS) + + - The first thing you should do, is **set a [team](https://github.com/AI21/template/blob/main/.github/CODEOWNERS) as a code owner for the `.github/settings.yml`**. + +- [ProBot Settings](https://github.com/probot/settings) — Synchronize repository settings defined in [`.github/settings.yml`](.github/settings.yml) to GitHub, enabling Pull Requests for repository settings + + When adding/removing/modifying jobs within workflows, you might need to tweak the *required status checks* in this file; the required status checks must pass before you can merge your branch into the protected branch. + +- [ProBot Stale](https://github.com/probot/stale/) — Closes abandoned Issues and Pull Requests after a period of inactivity as defined in [`.github/stale.yml`](.github/stale.yml) +- [DependaBot](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates) — Alerts on security vulnerabilities within the repository's dependencies, and updates the dependencies automatically as defined in [`.github/dependabot.yml`](.github/dependabot.yml) +- [RenovateBot](https://github.com/renovatebot/renovate) — Universal dependency update tool as defined in [`.github/renovate.json`](.github/renovate.json) ([application dashboard](https://app.renovatebot.com)) +- [Pre-commit](https://pre-commit.com/) — Managing and maintaining multi-language pre-commit hooks as defined in [`.pre-commit-config.yaml`](.pre-commit-config.yaml) + + - Leverage `pre-commit` to [install the Git hooks](https://pre-commit.com/#pre-commit-install) + + ```shell + pre-commit install --install-hooks -t pre-commit -t commit-msg + ``` + + - You can check which files `pre-commit` works on by running + + ```shell + pre-commit run list-files --hook-stage manual --verbose + ``` + +- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) — An easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of. Such as generating a [changelog](https://keepachangelog.com/) + + ```shell + git cz changelog + ``` + +--- + +## Automation Features + +We **strongly recommend** to enable the following features manually + +### Code review assignment + +[Code review assignments clearly indicate which members of a team are expected to submit a review for a pull request](https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-assignment-for-your-team). + +### Scheduled reminders + +You can get reminders in Slack when your [team has pull requests waiting for review](https://docs.github.com/en/organizations/organizing-members-into-teams/managing-scheduled-reminders-for-your-team#creating-a-scheduled-reminder-for-a-team) or for [your user with real-time alerts](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/managing-your-membership-in-organizations/managing-your-scheduled-reminders). + +## Prerequisites + +We are using a collection of tools. In order to work with this repository please [configure your Mac](https://github.com/AI21/dev-envs#getting-started) + +## Quick Start + +Run [`./bootstrap.sh`](./bootstrap.sh) and follow the interactive on-screen instructions. For more information, Run `./bootstrap.sh --help`. + +This will install pre-commit hooks, modify relevant files, deletes itself and open a draft pull-request. + +## Syncing with the Template repository + +To keep your repository up-to-date with the template git repository, execute the following from within your repository's root directory + +```shell +git clone git@github.com:AI21/template.git ../template +git checkout -b template-sync +rsync -ax --exclude .git --exclude README.md --exclude CHANGELOG.md --exclude bootstrap.sh ../template/ . +``` + +Then do cherry-picking for the changes which you would like to merge. + +--- + +## Modify this README to suit the project diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 00000000..2f8feb18 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash + +set -u -e -o pipefail + +REPO_TEAM='' +REPO_DESC='' +REPO_NAME="$(basename "$PWD")" +REPO_README=true + +function usages() { + cat <] repository team (default: "$REPO_TEAM") + [-d|--desc ] repository description (default: "$REPO_DESC") + [-n|--name ] repository name (default: "$REPO_NAME") + [-r|--readme produce a minimal readme (default: "$REPO_README") + [-h|--help] shows this help message +EOM + exit 0 +} + +# parse arguments +# https://stackoverflow.com/a/33826763 +while [[ $# -gt 0 ]]; do + case $1 in + -t | --team) + REPO_TEAM="$2" + shift + ;; + -d | --desc) + REPO_DESC="$2" + shift + ;; + -n | --name) + REPO_NAME="$2" + shift + ;; + -h | --help) + usages + ;; + -r | --readme) + REPO_README="$2" + shift + ;; + *) + echo "Unknown parameter passed: $1" + exit 1 + ;; + esac + shift +done + +# install pre-commit +pre-commit install --install-hooks -t pre-commit -t commit-msg --overwrite +echo + +if [ -z "$REPO_TEAM" ]; then + # read a team + TEAMS="$(grep "@DynamicYield" .github/CODEOWNERS | sed 's/^# //g')" + COLUMNS=1 + PS3=$'\n'"Pick a team number: " + select REPO_TEAM in $TEAMS; do break; done +else + echo "Repository team: ${REPO_TEAM}" +fi + +# define code-ownership +echo "* $REPO_TEAM" >>.github/CODEOWNERS + +# define team permissions for code-ownership +REPO_TEAM_SHORT="$(echo "$REPO_TEAM" | cut -d / -f 2)" +PATCH=$( + cat <<-EOS +teams: + - name: $REPO_TEAM_SHORT + permission: push +EOS +) +perl -i -p0e "s/^teams:/$PATCH/m" .github/settings.yml + +if [ -z "$REPO_NAME" ]; then + # read repository name + read -erp "Repository name: " -i "$REPO_NAME" REPO_NAME +fi + +# set repository name +perl -i -p0e "s/# name: template/name: $REPO_NAME/m" .github/settings.yml + +if [ -z "$REPO_DESC" ]; then + # read repository description + read -erp "Repository description: " -i "$REPO_DESC" REPO_DESC +else + echo "Repository description: ${REPO_DESC}" +fi + +# set repository description +perl -i -p0e "s/description: GitHub Template Repository/description: $REPO_DESC/m" .github/settings.yml + +# # delete this script +rm -f "$0" + +# settings pr +git checkout main +git checkout -b settings +git add .github/settings.yml bootstrap.sh +git commit -S -m "build(repo): settings" -q +git push origin "$(git branch --show-current)" +gh pr create --base main --draft --body "# merge this first!" --title "build(repo): settings" --fill + +# codeowners pr +git checkout main +git checkout -b codeowners +git add .github/CODEOWNERS +git commit -S -m "build(repo): codeowners" -q +git push origin "$(git branch --show-current)" +gh pr create --base main --draft --body "# merge this second! (after #1)" --title "build(repo): codeowners" --fill + +if [ "$REPO_README" = "true" ]; then + # readme + cat >README.md <