diff --git a/.editorconfig b/.editorconfig index 0525a15..d1790b6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,7 @@ trim_trailing_whitespace = true # Markdown [*.md] trim_trailing_whitespace = false + +[/.githooks/**] +end_of_line = lf +insert_final_newline = false diff --git a/.gitattributes b/.gitattributes index 0c42f3c..35b1ebf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ * text=auto eol=crlf + +# Force githooks to be LF +.githooks/* eol=lf diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100644 index 0000000..6e35aeb --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,3 @@ +#!/bin/sh +COMMIT_MSG_FILE=$1 +pwsh -ExecutionPolicy RemoteSigned -File 'scripts/githooks/commit-msg-check.ps1' "$COMMIT_MSG_FILE" \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 0000000..5171eed --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,2 @@ +#!/bin/sh +pwsh -ExecutionPolicy RemoteSigned -File 'scripts/githooks/pre-commit-file-list.ps1' \ No newline at end of file diff --git a/.github/workflows/lab00-hello-github-actions.yml b/.github/workflows/lab00-hello-github-actions.yml index bc692b1..6b336e7 100644 --- a/.github/workflows/lab00-hello-github-actions.yml +++ b/.github/workflows/lab00-hello-github-actions.yml @@ -1,11 +1,40 @@ +# Name of the workflow name: Lab00 Hello GitHub Actions +# How the workflow run is named in GitHub run-name: Lab00 - ${{ github.actor }} is testing out GitHub Actions 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: +# The actual jobs jobs: - example-job: + # Job named Hello-job that runs on ubuntu-latest virtual machine + Hello-Job: runs-on: ubuntu-latest + # Steps that are being run to complete the job steps: - name: Run hello world run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Job named Build-Job that builds the example applications + Build-Job: + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + - name: Install .NET dependencies + run: dotnet restore + - name: Build application + run: dotnet build diff --git a/.github/workflows/lab01-variables-and-secrets.yml b/.github/workflows/lab01-variables-and-secrets.yml index b783ea9..509d40d 100644 --- a/.github/workflows/lab01-variables-and-secrets.yml +++ b/.github/workflows/lab01-variables-and-secrets.yml @@ -4,8 +4,28 @@ permissions: read-all on: workflow_dispatch: jobs: - example-job: + demo: runs-on: ubuntu-latest + # Define a global environment variable for this job + env: + MY_VARIABLE: "Hello from the environment variable!" + steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Echo Variable and Secret + env: + # For this to work, you need to create a repository secret on GitHub.com + # 1. Go to your repository on GitHub.com + # 2. Navigate to Settings > Secrets and variables > Actions + # 3. Click "New repository secret" + # 4. Name: DEMO_SECRET + # 5. Secret: your-secret-value + + # Assign the repository secret to an environment variable for this step + DEMO_SECRET: ${{ secrets.DEMO_SECRET }} + run: | + echo "Environment Variable: $MY_VARIABLE" + echo "Secret Value: $DEMO_SECRET" + echo "Note how the secret value is not printed to the log" diff --git a/.github/workflows/lab02-reuse-and-artifacts.yml b/.github/workflows/lab02-reuse-and-artifacts.yml index 9728fe9..45631b1 100644 --- a/.github/workflows/lab02-reuse-and-artifacts.yml +++ b/.github/workflows/lab02-reuse-and-artifacts.yml @@ -4,8 +4,25 @@ permissions: read-all on: workflow_dispatch: jobs: - example-job: + run-composite: runs-on: ubuntu-latest steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Checkout the repository to the GitHub Actions runner + - name: Checkout + uses: actions/checkout@v4 + # Run the template with parameters + - id: dotnet-build + uses: ./.github/actions/build-dotnet-app + with: + folder-path: "apps/uptodate-app" + # Publish output folder as an artifact + - name: Publish Artifact + uses: actions/upload-artifact@v4 + with: + name: dotnet-app + path: ${{ steps.dotnet-build.outputs.output-directory-path }} + + greeter: + uses: ./.github/workflows/greeter.yml + with: + person-name: "${{ github.actor }}" diff --git a/.github/workflows/lab10-software-composition-analysis.yml b/.github/workflows/lab10-software-composition-analysis.yml index a83ff57..cde3398 100644 --- a/.github/workflows/lab10-software-composition-analysis.yml +++ b/.github/workflows/lab10-software-composition-analysis.yml @@ -1,11 +1,136 @@ +# Name of the workflow name: Lab10 SCA +# How the workflow run is named in GitHub run-name: Lab10 - ${{ github.actor }} is finding vulns🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: + # Dependency review is meant to work with prs + pull_request: jobs: - example-job: + Test-Vulnerabilities-With-Dependency-Check: + name: OWASP Dependency Check runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + # Ensure this to make sure to actually test against real deal + - name: Install .NET dependencies + run: dotnet restore + # Sometimes (even when considered as a bad practice) part of the dependencies are fetched during build, so better to build first + - name: Build application + run: dotnet build + # Run OWASP Dependency check + - name: Dependency Check + uses: dependency-check/Dependency-Check_Action@main + id: Depcheck + with: + project: "ci-security" + path: "." + format: "HTML" + out: "reports" # this is the default, no need to specify unless you wish to override it + args: > + --failOnCVSS 7 + --enableRetired + # Use always() to ensure that even if the above fails on threshold the results are uploaded + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: OWASP Dependency Check report + path: ${{github.workspace}}/reports + + Test-Vulnerabilities-Natively: + name: "dotnet native check" + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + # Ensure this to make sure to actually test against real deal + - name: Install .NET dependencies + run: dotnet restore + # Sometimes (even when considered as a bad practice) part of the dependencies are fetched during build, so better to build first + - name: Build application + run: dotnet build + - name: Use dotnet tools to test vulns and to fail the job + run: | # Use | to tell github actions that this is multiline operation + $vulnCount = .\scripts\dotnetaudit.ps1 + Write-Host "Run dotnetaudit.ps1 with total of $vulnCount vulnerabilities" + Write-Host "---OUTPUT---" + Get-content vulnerable.out + if ($vulnCount -gt 0) { + # Set proper error message + echo "::error file=scripts/dotnetaudit.ps1,line=19,col=1,endColumn=21,title=Vulnerabilities above zero::Vulnerabilities found for total of $vulnCount" + # Error message does not fail the job, use exit 1 to actually make it fail + exit 1 + } + + Test-Vulnerabilities-With-RetireJS: + name: "RetireJS check" + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + # Ensure this to make sure to actually test against real deal + - name: Install .NET dependencies + run: dotnet restore + # Sometimes (even when considered as a bad practice) part of the dependencies are fetched during build, so better to build first + - name: Build application + run: dotnet build + # Setup Node + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "23.x" + - name: Setup RetireJS and create folder for reports + run: | + npm install -g retire + mkdir reports + - name: Run RetireJS + run: retire --path . --outputformat text --outputpath ./reports/output.txt --severity low --exitwith 1 --deep + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: RetireJS report + path: ${{github.workspace}}/reports + + Test-with-dependency-review: + runs-on: ubuntu-latest + steps: + - name: "Checkout Repository" + uses: actions/checkout@v4 + - name: "Dependency Review" + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: low + #allow-licenses: MIT + #deny-licenses: AGPL diff --git a/.github/workflows/lab11-create-package-lock.yml b/.github/workflows/lab11-create-package-lock.yml index cbe0c99..18172e0 100644 --- a/.github/workflows/lab11-create-package-lock.yml +++ b/.github/workflows/lab11-create-package-lock.yml @@ -1,11 +1,44 @@ +# Name of the workflow name: Lab11 Package Locks +# How the workflow run is named in GitHub run-name: Lab11 - ${{ github.actor }} is locking packages 🔒 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: +# The actual jobs jobs: - example-job: + Create-Package-Locks: + name: "Create Package Lock" runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + # Generate lock file + - name: Generate lock file + run: dotnet restore --force --use-lock-file + # Test restore with locked mode + - name: Restore with locked mode + run: dotnet restore --locked-mode + # Dotnet by default restores implicitly if missing something. It is unneccessary and unwanted in CI-situations. + - name: Build application + run: dotnet build --no-restore + - name: Print the package locks + run: | + get-childitem *.lock.json -Recurse | % { + Write-Host $_ + Get-Content $_ + } diff --git a/.github/workflows/lab12-license-check.yml b/.github/workflows/lab12-license-check.yml index e59026f..aa936a5 100644 --- a/.github/workflows/lab12-license-check.yml +++ b/.github/workflows/lab12-license-check.yml @@ -1,11 +1,106 @@ +# Name of the workflow name: Lab12 License check +# How the workflow run is named in GitHub run-name: Lab12 - ${{ github.actor }} is inspecting used licenses 🔎 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + Check-licensed-with-delice: + name: "Licenses: delice" runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + - name: Restore + run: dotnet restore + - name: Setup delice + run: dotnet tool install -g dotnet-delice + - name: Check licenses with Delice + run: | + $unwantedLicenses = ./scripts/licenseaudit.ps1 | Where-Object { $_ -ne "MIT" } + if ($unwantedLicenses -gt 0) { + # Set proper error message + echo "::error file=scripts/licenseaudit.ps1,title=Unwanted licenses detected::Unwanted licenses detected: $unwantedLicenses" + # Error message does not fail the job, use exit 1 to actually make it fail + exit 1 + } + + Check-licensed-with-project-licenses: + name: "Licenses: dotnet-project-licenses" + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "7.x.x" + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + - name: Restore + run: dotnet restore + - name: Setup dotnet-project-licenses + run: dotnet tool install -g dotnet-project-licenses + - name: Check licenses with dotnet-project-licenses + run: | + $allowedLicenses = "[`"MIT`"]" | Out-File allowed.json + dotnet-project-licenses --input . --allowed-license-types allowed.json + + Check-licensed-with-scancode: + name: "Licenses: scancode" + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + with: + path: scancode-inputs + # Ensure proper version of .NET + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "8.0.x" + - name: Restore + run: dotnet restore scancode-inputs/ci-security.sln + - name: Install ScanCode + run: pip install scancode-toolkit[full] + - name: Create folder for reports + run: mkdir reports + - name: Run ScanCode + run: scancode --license --email --license-score 70 -n 10 ./scancode-inputs --html reports/output.html --json-pp reports/output.json + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: Scancode report + path: ${{github.workspace}}/reports + - name: Check licenses found with scancode + run: | + $unwantedLicenses = ./scancode-inputs/scripts/scancodeaudit.ps1 ./reports/output.json | Where-Object { $_ -ne "MIT" } + if ($unwantedLicenses -gt 0) { + # Set proper error message + echo "::error file=scripts/scancodeaudit.ps1,title=Unwanted licenses detected::Unwanted licenses detected: $unwantedLicenses" + # Error message does not fail the job, use exit 1 to actually make it fail + exit 1 + } diff --git a/.github/workflows/lab13-sbom.yml b/.github/workflows/lab13-sbom.yml index c801706..186258a 100644 --- a/.github/workflows/lab13-sbom.yml +++ b/.github/workflows/lab13-sbom.yml @@ -1,11 +1,60 @@ +# Name of the workflow name: Lab13 SBOM +# How the workflow run is named in GitHub run-name: Lab13 - ${{ github.actor }} is running SBOM scans 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + cyclonedx: runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - name: Checkout + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 8.0.x + - name: Install CycloneDX + run: dotnet tool install --global CycloneDX + - name: Generate sbom with CycloneDX + run: dotnet CycloneDX ci-security.sln --json --exclude-dev -o ./cyclone-reports + - name: Upload sbom results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: CycloneDX SBOM + path: ${{github.workspace}}/cyclone-reports + spdx: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 8.0.x + - name: Install Microsoft SBOM tool + run: dotnet tool install --global Microsoft.Sbom.DotNetTool + - name: Create SBOM reports directory + run: mkdir sbom-reports + - name: Install .NET dependencies + run: dotnet restore + - name: Build application + run: dotnet build + - name: Generate SBOM + run: sbom-tool generate -PackageSupplier "Ci-Security Example ltd" -BuildComponentPath ./apps -PackageName ExamplePackage -PackageVersion 1 -BuildDropPath ./sbom-reports + - name: Upload sbom results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: SPDX SBOM + path: ${{github.workspace}}/sbom-reports diff --git a/.github/workflows/lab20-list-branch-protections.yml b/.github/workflows/lab20-list-branch-protections.yml index e9fb180..2a64f8c 100644 --- a/.github/workflows/lab20-list-branch-protections.yml +++ b/.github/workflows/lab20-list-branch-protections.yml @@ -1,11 +1,25 @@ +# Name of the workflow name: Lab20 List branch protections +# How the workflow run is named in GitHub run-name: Lab20 - ${{ github.actor }} is listing branch protections with GraphQL 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: +# The actual jobs jobs: - example-job: + Branch-Protections: runs-on: ubuntu-latest steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - name: Checkout + uses: actions/checkout@v4 + - name: Check branch protections + run: ./scripts/branchprotections.ps1 + env: + GH_TOKEN: ${{ secrets.READ_PAT }} diff --git a/.github/workflows/lab21-static-application-security-testing.yml b/.github/workflows/lab21-static-application-security-testing.yml index ea74b00..684f532 100644 --- a/.github/workflows/lab21-static-application-security-testing.yml +++ b/.github/workflows/lab21-static-application-security-testing.yml @@ -1,11 +1,83 @@ +# Name of the workflow name: Lab21 SAST +# How the workflow run is named in GitHub run-name: Lab21 - ${{ github.actor }} is finding vulns 🔎 permissions: read-all on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + SAST-with-Semgrep: + name: "SAST: Semgrep CE" runs-on: ubuntu-latest + container: + # A Docker image with Semgrep installed. Do not change this. + image: semgrep/semgrep steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Fetch project source with GitHub Actions Checkout. Use either v3 or v4. + - uses: actions/checkout@v4 + # Run the "semgrep scan" command on the command line of the docker image. + - run: semgrep scan --config auto + + SAST-with-GHAS: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + # required for all workflows + security-events: write + # required to fetch internal or private CodeQL packs + packages: read + strategy: + fail-fast: false + matrix: + include: + - language: csharp + build-mode: autobuild + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/lab22-secret-scanning.yml b/.github/workflows/lab22-secret-scanning.yml index 7b9cbb5..6e2a6b4 100644 --- a/.github/workflows/lab22-secret-scanning.yml +++ b/.github/workflows/lab22-secret-scanning.yml @@ -1,11 +1,36 @@ +# Name of the workflow name: Lab22 Secret scanning +# How the workflow run is named in GitHub run-name: Lab22 - ${{ github.actor }} is looking for keys 🔑 permissions: read-all on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + secret-scanning-with-gitleaks: + name: gitleaks runs-on: ubuntu-latest steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + secret-scanning-with-trufflehog: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Secret Scanning + uses: trufflesecurity/trufflehog@main + with: + # Note that verified means the secret is known to be valid, so the secrets in this repo won't be flagged + extra_args: --results=verified,unverified,unknown diff --git a/.github/workflows/lab30-infrastructure-as-code-scanning.yml b/.github/workflows/lab30-infrastructure-as-code-scanning.yml index ee288b6..70d4a73 100644 --- a/.github/workflows/lab30-infrastructure-as-code-scanning.yml +++ b/.github/workflows/lab30-infrastructure-as-code-scanning.yml @@ -1,11 +1,49 @@ +# Name of the workflow name: Lab30 IaC Scanning +# How the workflow run is named in GitHub run-name: Lab30 - ${{ github.actor }} is looking for misconfigurations ⚙️ permissions: read-all on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + iac-scan-with-checkov: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + # The type of runner that the job will run on runs-on: ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Checks-out your repository under $GITHUB_WORKSPACE, so follow-up steps can access it + - uses: actions/checkout@v4 + + # Optional TF setup, as plain .tf files are also scanned + # - uses: hashicorp/setup-terraform@v3 + # - name: Init and Plan terraform + # run: | + # terraform init + # terraform plan -out=plan.tfplan + # working-directory: ./labs/lab08-iac-scanning + + - name: Checkov GitHub Action + uses: bridgecrewio/checkov-action@v12 + with: + # This will add both a CLI output to the console and create a results.sarif file + output_format: cli,sarif + output_file_path: console,results.sarif + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + + # Results are generated only on a success or failure + # this is required since GitHub by default won't run the next step + # when the previous one has failed. Security checks that do not pass will 'fail'. + # An alternative is to add `continue-on-error: true` to the previous step + # Or 'soft_fail: true' to checkov. + if: success() || failure() + with: + sarif_file: results.sarif diff --git a/.github/workflows/lab40-http-header-scanning.yml b/.github/workflows/lab40-http-header-scanning.yml index 35460f5..eef9844 100644 --- a/.github/workflows/lab40-http-header-scanning.yml +++ b/.github/workflows/lab40-http-header-scanning.yml @@ -1,11 +1,54 @@ +# Name of the workflow name: Lab40 HTTP header scanning +# How the workflow run is named in GitHub run-name: Lab40 - ${{ github.actor }} is scanning HTTP headers 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + venom-http-scanning: runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - name: Checkout + uses: actions/checkout@v4 + - name: Install venom + run: | + curl https://github.com/ovh/venom/releases/download/v1.1.0/venom.linux-amd64 -L -o /usr/local/bin/venom && chmod +x /usr/local/bin/venom + venom -h + - name: Run the app + run: | + Write-Host "-- Build docker" + docker build -t vulnerable-app-image -f ./apps/vulnerable-app/Dockerfile . + + Write-Host "-- Run docker" + $containerId = docker run --tty --detach vulnerable-app-image --publish 8080:8080 + + Write-Host "-- Fetch IP" + $targetIp = docker inspect --format '{{ .NetworkSettings.IPAddress }}' $containerId + Write-Host "TargetIP was $targetIp" + $target = "http://"+$targetIp+":8080" + + Write-Host "-- Wait for the docker for a moment" + Start-Sleep -Seconds 10 + + Write-Host "-- Fetch something from target" + curl $target + + Write-Host "-- Run venom" + venom run --var="target_site=$target" ./scripts/oshp_validator_tests_suite.yml + mdn-http-observatory-scanning: + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + - name: Install Mozilla Observatory + run: npm install --global @mdn/mdn-http-observatory + - name: Run Mozilla Observatory + run: mdn-http-observatory-scan huuhka.net diff --git a/.github/workflows/lab41-tls-scanning.yml b/.github/workflows/lab41-tls-scanning.yml index 24f8224..0a4e2a9 100644 --- a/.github/workflows/lab41-tls-scanning.yml +++ b/.github/workflows/lab41-tls-scanning.yml @@ -1,11 +1,120 @@ +# Name of the workflow name: Lab41 TLS scanning +# How the workflow run is named in GitHub run-name: Lab41 - ${{ github.actor }} is scanning TLS ciphers 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + nmap-scan: runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Nmap + run: sudo apt install nmap + - name: Scan TLS for given domain + run: | + + $password = "OH_NOES_EXPOSED_PASSWORD_IS_BAD" + $httpsPort = "8001" + Write-Host "-- Build docker" + docker build -t vulnerable-app-image -f ./apps/vulnerable-app/Dockerfile . + + Write-host "-- Create HTTPS certificate" + mkdir cert + dotnet dev-certs https -ep ./cert/secret_certificate.pfx -p $password + dotnet dev-certs https --trust + + Write-Host "-- Run docker" + $containerId = docker run --tty --detach -p ($httpsPort+":"+$httpsPort) -e ASPNETCORE_HTTPS_PORTS=$httpsPort -e ASPNETCORE_ENVIRONMENT=Development -v ${HOME}/.microsoft/usersecrets/:/home/app/.microsoft/usersecrets -v ./cert/:/https/ -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/secret_certificate.pfx -e ASPNETCORE_Kestrel__Certificates__Default__Password=$password vulnerable-app-image + + Write-Host "-- Wait for the docker for a moment" + Start-Sleep -Seconds 10 + + Write-Host "-- Fetch IP" + $targetIp = docker inspect --format '{{ .NetworkSettings.IPAddress }}' $containerId + Write-Host "-- TargetIP was $targetIp" + $target = "https://"+$targetIp+":$httpsPort" + + Write-Host "-- Fetch something from target" + curl --insecure $target + + Write-Host "-- Start TLS scan" + $scanResults = ./scripts/tlsaudit.ps1 $targetIp $httpsPort + $grade = $scanResults[0] + $ciphers = $scanResults[1] + + Write-Host "---- CIPHERS ----" + For ($i = 1; $i -le $scanResults.Length; $i++) { + Write-Host $scanResults[$i] + } + Write-Host "----- GRADE -----" + Write-Host $grade + if ($grade -ne "A") { + # Set proper error message + echo "::error file=scripts/tlsaudit.ps1,title=Weak ciphers detected::Weak ciphers detected with grade: $grade" + # Error message does not fail the job, use exit 1 to actually make it fail + exit 1 + } + testssl-scan: + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + # Check out the git repository + - name: Checkout + uses: actions/checkout@v4 + - name: Scan TLS for given domain + run: | + $password = "ANOTHER_LEAKED_PASSWORD_IS_DOUBLE_BAD" + $httpsPort = "8001" + Write-Host "-- Build docker" + docker build -t vulnerable-app-image -f ./apps/vulnerable-app/Dockerfile . + + Write-host "-- Create HTTPS certificate" + mkdir cert + dotnet dev-certs https -ep ./cert/secret_certificate.pfx -p $password + dotnet dev-certs https --trust + + Write-Host "-- Run docker" + $containerId = docker run --tty --detach -p ($httpsPort+":"+$httpsPort) -e ASPNETCORE_HTTPS_PORTS=$httpsPort -e ASPNETCORE_ENVIRONMENT=Development -v ${HOME}/.microsoft/usersecrets/:/home/app/.microsoft/usersecrets -v ./cert/:/https/ -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/secret_certificate.pfx -e ASPNETCORE_Kestrel__Certificates__Default__Password=$password vulnerable-app-image + + Write-Host "-- Wait for the docker for a moment" + Start-Sleep -Seconds 10 + + Write-Host "-- Fetch IP" + $targetIp = docker inspect --format '{{ .NetworkSettings.IPAddress }}' $containerId + Write-Host "-- TargetIP was $targetIp" + $target = "https://"+$targetIp+":$httpsPort" + + Write-Host "-- Fetch something from target" + curl --insecure $target + + Write-Host "-- Create results folder" + mkdir ${{github.workspace}}/testsslreports + + Write-Host "-- Chmod results folder rights" + chmod -R a+rw ${{github.workspace}}/testsslreports + + Write-Host "-- Start TLS scan" + docker run -v ${{github.workspace}}/testsslreports:/testsslreports:rw --rm drwetter/testssl.sh --jsonfile /testsslreports $target + + Write-Host "-- Check the actual vulns from the report (you could do it with parameters too)" + $resultsFile = Get-ChildItem -Path ${{github.workspace}}/testsslreports -Force -Recurse -File | Select-Object -First 1 + Get-Content $resultsFile | ConvertFrom-Json | Where-Object { $_.severity -ne "INFO" -and $_.severity -ne "OK" } + + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: testssl report + path: ${{github.workspace}}/testsslreports diff --git a/.github/workflows/lab42-dast.yml b/.github/workflows/lab42-dast.yml index 170ea7f..1d2bbd2 100644 --- a/.github/workflows/lab42-dast.yml +++ b/.github/workflows/lab42-dast.yml @@ -1,11 +1,143 @@ +# Name of the workflow name: Lab42 DAST +# How the workflow run is named in GitHub run-name: Lab42 - ${{ github.actor }} is running DAST scans 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + zap-scanning: runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - name: Checkout + uses: actions/checkout@v4 + - name: Build docker image and run ZAP scan + run: | + Write-Host "-- Build docker" + docker build -t vulnerable-app-image -f ./apps/vulnerable-app/Dockerfile . + + Write-Host "-- Run docker" + $containerId = docker run --tty --detach vulnerable-app-image --publish 8080:8080 + + Write-Host "-- Fetch IP" + $targetIp = docker inspect --format '{{ .NetworkSettings.IPAddress }}' $containerId + Write-Host "TargetIP was $targetIp" + $target = "http://"+$targetIp+":8080" + + Write-Host "-- Wait for the docker for a moment" + Start-Sleep -Seconds 10 + + Write-Host "-- Create results folder" + mkdir ${{github.workspace}}/zapreports + + Write-Host "-- Chmod results folder rights" + chmod -R a+rw ${{github.workspace}}/zapreports + + Write-Host "-- Run docker ZAP" + docker run -v ${{github.workspace}}/zapreports:/zap/wrk:rw -t ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py -t $target -J report_json.json -w report_md.md -r report_html.html -a + + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: ZAP report + path: ${{github.workspace}}/zapreports + # Commented out because it looked like the next step did not have anymore network access to last steps running target + # Still pretty handy if you have live target + # - name: ZAP Scan + # uses: zaproxy/action-full-scan@v0.12.0 + # env: + # ZAP_TARGET: + # with: + # token: ${{ secrets.GITHUB_TOKEN }} # to create issues in repository + # docker_name: "ghcr.io/zaproxy/zaproxy:stable" + # target: "http://${{ steps.docker-build.outputs.ZAP_TARGET_IP }}" + # cmd_options: "-a" + nuclei-scanning: + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build docker image and run nuclei scan + run: | + Write-Host "-- Build docker" + docker build -t vulnerable-app-image -f ./apps/vulnerable-app/Dockerfile . + + Write-Host "-- Run docker" + $containerId = docker run --tty --detach vulnerable-app-image --publish 8080:8080 + + Write-Host "-- Fetch IP" + $targetIp = docker inspect --format '{{ .NetworkSettings.IPAddress }}' $containerId + Write-Host "TargetIP was $targetIp" + $target = "http://"+$targetIp+":8080" + + Write-Host "-- Wait for the docker for a moment" + Start-Sleep -Seconds 10 + + Write-Host "-- Create results folder" + mkdir ${{github.workspace}}/nucleireports + + Write-Host "-- Chmod results folder rights" + chmod -R a+rw ${{github.workspace}}/nucleireports + + Write-Host "-- Run docker ZAP" + docker run -v ${{github.workspace}}/nucleireports:/reports:rw -t projectdiscovery/nuclei:latest -u $target -j -v -o /reports/scan.json + + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: Nuclei report + path: ${{github.workspace}}/nucleireports + + ffuf-scanning: + runs-on: ubuntu-latest + # Steps to actually build the application + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install latest go + uses: actions/setup-go@v5 + with: + go-version: "stable" # Latest stable: https://github.com/actions/setup-go + - name: Install ffuf + run: go install github.com/ffuf/ffuf/v2@latest + - name: Build docker image and run ffuf scan + run: | + Write-Host "-- Build docker" + docker build -t vulnerable-app-image -f ./apps/vulnerable-app/Dockerfile . + + Write-Host "-- Run docker" + $containerId = docker run --tty --detach vulnerable-app-image --publish 8080:8080 + + Write-Host "-- Fetch IP" + $targetIp = docker inspect --format '{{ .NetworkSettings.IPAddress }}' $containerId + Write-Host "TargetIP was $targetIp" + $target = "http://"+$targetIp+":8080/FUZZ" + + Write-Host "-- Wait for the docker for a moment" + Start-Sleep -Seconds 10 + + Write-Host "-- Create results folder" + mkdir ${{github.workspace}}/ffufreports + + Write-Host "-- Download fuzz wordlist" + Invoke-WebRequest https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/IIS.fuzz.txt -OutFile ./iisfuzz.txt + Write-Host "-- Fuzz with ffuf" + ffuf -w ./iisfuzz.txt -u $target -json | Out-File "${{github.workspace}}/ffufreports/fuzzoutput.json" + + - name: Upload Test results + uses: actions/upload-artifact@master + if: ${{ always() }} + with: + name: ffuf report + path: ${{github.workspace}}/ffufreports diff --git a/.github/workflows/lab50-sarif.yml b/.github/workflows/lab50-sarif.yml index dc8ddec..f626829 100644 --- a/.github/workflows/lab50-sarif.yml +++ b/.github/workflows/lab50-sarif.yml @@ -1,11 +1,39 @@ +# Name of the workflow name: Lab50 SARIF +# How the workflow run is named in GitHub run-name: Lab50 - ${{ github.actor }} is creating SARIF reports 🚀 permissions: read-all +# Set your preferences for the script +defaults: + run: + shell: pwsh on: + # Make it possible to other workflows to call this + workflow_call: + # Make it possible to manually run this workflow workflow_dispatch: jobs: - example-job: + http-header-scanning: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status runs-on: ubuntu-latest + # Steps to actually build the application steps: - - name: Run hello world - run: echo "🎉 Step of the job is running on ${{ runner.os }}!" + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.x" + - name: Install Mozilla Observatory + run: npm install --global @mdn/mdn-http-observatory + - name: Checkout + uses: actions/checkout@v4 + - name: Run Mozilla Observatory and create SARIF results + run: ./scripts/mozilla-observatory-sarif-generator.ps1 "huuhka.net" "mdn.sarif.json" + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + if: success() || failure() + with: + sarif_file: "mdn.sarif.json" + category: mdn-sarif diff --git a/README.md b/README.md index 669e8ce..da17320 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,6 @@ Apps are pretty boring. - [Lab20 - Branch protections](/labs/lab2x-development/lab20-branch-protections/README.md) - [Lab21 - Static Application Security Testing](/labs/lab2x-development/lab21-sast/README.md) - [Lab22 - Secret scanning](/labs/lab2x-development/lab22-secret-scanning/README.md) -- [Lab23 - Prevent email address disclosure](/labs/lab2x-development/lab23-prevent-email-disclosure/README.md) - [Lab30 - IaC scanning](/labs/lab3x-infrastructure/lab30-iac-scanning/README.md) - [Lab40 - HTTP header scanning](/labs/lab4x-testing-live-target/lab40-http-header-scanning/README.md) - [Lab41 - TLS cipher scanning](/labs/lab4x-testing-live-target/lab41-tls-scanning/README.md) diff --git a/configure-githooks.ps1 b/configure-githooks.ps1 new file mode 100644 index 0000000..a68d0a0 --- /dev/null +++ b/configure-githooks.ps1 @@ -0,0 +1,5 @@ +[CmdletBinding()] +param() + +# Config git hooks +git config core.hooksPath .githooks diff --git a/scripts/branchprotections.ps1 b/scripts/branchprotections.ps1 new file mode 100644 index 0000000..23d89df --- /dev/null +++ b/scripts/branchprotections.ps1 @@ -0,0 +1,20 @@ +# Check the fields you want from https://docs.github.com/en/graphql/reference/objects#repositoryruleset +$query = @" +query { + repository(name:"ci-security", owner:"Rinorragi") { + rulesets(first: 100) { + nodes { + name + target + rules(first: 100) { + nodes { + type + } + } + } + } + } +} +"@ + +gh api graphql -f query="$query" diff --git a/scripts/dotnetaudit.ps1 b/scripts/dotnetaudit.ps1 new file mode 100644 index 0000000..4dd39d5 --- /dev/null +++ b/scripts/dotnetaudit.ps1 @@ -0,0 +1,19 @@ +# Remove unneccessary text from output with null +# Run dotnet list package including transitives and output that +$null = dotnet list package --vulnerable --include-transitive | Tee-Object vulnerable.out + +function CalculateVulnLines { + param($RegexString) + $vulnsResult = get-content .\vulnerable.out | Select-String -Pattern $RegexString + $vulnCount = $vulnsResult | Measure-Object -Line + $vulnCount.Lines +} + +$vCritical = CalculateVulnLines " Critical " +$vHigh = CalculateVulnLines " High " +$vModerate = CalculateVulnLines " Moderate " +$vLow = CalculateVulnLines " Low " + +# Return the amount of vulnerabilities +$vTotal = $vCritical + $vHigh + $vModerate + $vLow +Write-Output $vTotal diff --git a/scripts/githooks/commit-msg-check.ps1 b/scripts/githooks/commit-msg-check.ps1 new file mode 100644 index 0000000..d407520 --- /dev/null +++ b/scripts/githooks/commit-msg-check.ps1 @@ -0,0 +1,17 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true, Position = 0)]$commitMsgFile +) + +$commitMessage = Get-Content $commitMsgFile +$ticketPrefix = "Lab" +$allowedKeywords = @("Add", "Update", "Remove", "Refactor", "Rename", "Move", "Fix") -join '|' + +# match to "-: something" syntax +$regexString = "^" + $ticketPrefix + "-\d+: ($allowedKeywords) .{2,}" +if (($commitMessage -match $regexString)) { + exit 0 # success +} + +Write-Host "Commit message has to follow '$ticketPrefix-: <$allowedKeywords> something' syntax." -ForegroundColor Red +exit 1 # failure diff --git a/scripts/githooks/pre-commit-file-list.ps1 b/scripts/githooks/pre-commit-file-list.ps1 new file mode 100644 index 0000000..ec235ec --- /dev/null +++ b/scripts/githooks/pre-commit-file-list.ps1 @@ -0,0 +1,14 @@ +[CmdletBinding()] +param() + +# Staged files that are about to be committed +$fileList = git diff --cached --name-only + +Write-Host "These are the files that you have modified or are about to add:" -ForegroundColor Green +Write-Host "---------------------------------------------------------------" +$fileList | ForEach-Object { Write-Host $_ -ForegroundColor Green } +Write-Host "---------------------------------------------------------------" +Write-Host "You could do some analysis here before commit happens" -ForegroundColor Yellow + +exit 0 # Commit succeed +# exit 1 # Commit would fail diff --git a/scripts/licenseaudit.ps1 b/scripts/licenseaudit.ps1 new file mode 100644 index 0000000..2385fae --- /dev/null +++ b/scripts/licenseaudit.ps1 @@ -0,0 +1,4 @@ +$licenseJson = dotnet delice ./ci-security.sln --json | ConvertFrom-Json +$ReturnArray = @() +$licenseJson.Projects | ForEach-Object { $_.licenses | Select-Object -ExpandProperty expression | ForEach-Object { $ReturnArray += $_ } } +$ReturnArray diff --git a/scripts/mozilla-observatory-sarif-generator.ps1 b/scripts/mozilla-observatory-sarif-generator.ps1 new file mode 100644 index 0000000..9b3af69 --- /dev/null +++ b/scripts/mozilla-observatory-sarif-generator.ps1 @@ -0,0 +1,120 @@ +param( + # Which domain to scan + [Parameter(Mandatory = $true)] + $domain, + # Where to generate the report + [Parameter()] + [string] + $fileName = "mdn.sarif.json" +) + +function Strip-String { + param( + [Parameter()] + $stringToStip) + ($stringToStip + "").Replace("\r", "").Replace("\n", "").Replace("

", "").Replace("

", "").Trim() +} + +$mozillaResultJson = mdn-http-observatory-scan $domain + +# Convert JSON to PS Object +$mozillaTestResults = +$mozillaResultJson +| ConvertFrom-Json +| Select-Object -ExpandProperty tests + +# Convert PSObject graph into array of custom objects +$testResults = +$mozillaTestResults +| Get-Member -MemberType NoteProperty +| Select-Object -ExpandProperty Name +| ForEach-Object { + $test = $mozillaTestResults.$_ + [PSCustomObject]@{ + Pass = $test.pass + Name = Strip-String $test.title + MessageText = Strip-String $test.recommendation + Score = $test.score_modifier + RuleId = Strip-String $test.result + Description = Strip-String $test.score_description + Link = Strip-String $test.link + } +} +| Where-Object { $_.Pass -eq $False } # Take only failing results (SARIF does not exactly support pass=true type of results) + +$sarifResultJsonTemplate = @" +{ + "version": "2.1.0", + "`$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "version": "1.0", + "semanticVersion": "1.0.0", + "name": "MDN-SARIF", + "fullName": "Mozilla Observatory SARIF generator", + "informationUri": "https://github.com/Rinorragi/ci-security/blob/main/scripts/mozilla-observatory-sarif-generator.ps1", + "rules": [] + } + }, + "results": [] + } + ] +} +"@ + +$sarifResult = $sarifResultJsonTemplate | ConvertFrom-Json +$sarifPhysicalLocationTemplate = [PSCustomObject]@{ + physicalLocation = [PSCustomObject]@{ + artifactLocation = [PSCustomObject]@{ + uri = $domain + } + region = [PSCustomObject]@{ + startLine = 1 + startColumn = 1 + endLine = 9999 + endColumn = 9999 + } + } +} + +$null = +$testResults +| ForEach-Object { + # Add to results + $sarifResult.runs[0].results += [PSCustomObject]@{ + ruleId = ($_.RuleId + "") + level = "error" + kind = "fail" + message = [PSCustomObject]@{ + text = ($_.RuleId + "") + } + locations = @($sarifPhysicalLocationTemplate) + } + # Add to tool rules + $sarifResult.runs[0].tool.driver.rules += [PSCustomObject]@{ + id = ($_.RuleId + "") + name = ($_.Name + "") + shortDescription = [PSCustomObject]@{ + text = ($_.Description + "") + } + fullDescription = [PSCustomObject]@{ + text = ($_.Description + "") + } + help = [PSCustomObject]@{ + text = ($_.Description + "") + } + helpUri = ("https://developer.mozilla.org" + $_.Link) + properties = [PSCustomObject]@{ + precision = "very-high" + severity = "error" + "security-severity" = "6" + } + } +} + +$null = $sarifResult | ConvertTo-Json -Depth 10 | Out-File $fileName + + + diff --git a/scripts/oshp_validator_tests_suite.yml b/scripts/oshp_validator_tests_suite.yml new file mode 100644 index 0000000..1cba2c3 --- /dev/null +++ b/scripts/oshp_validator_tests_suite.yml @@ -0,0 +1,192 @@ +name: HTTP security response headers test suites +vars: + target_site: '' + logout_url: '' + request_timeout_in_seconds: 20 +testcases: + - name: Strict-Transport-Security + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Strict-Transport-Security ShouldNotBeNil + - result.headers.Strict-Transport-Security ShouldEqual "max-age=31536000; includeSubDomains" + - name: X-Frame-Options + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.X-Frame-Options ShouldNotBeNil + - result.headers.X-Frame-Options ShouldBeIn "deny" "DENY" + - name: X-Content-Type-Options + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.X-Content-Type-Options ShouldNotBeNil + - result.headers.X-Content-Type-Options ShouldEqual "nosniff" + - name: Content-Security-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Content-Security-Policy ShouldNotBeNil + - result.headers.Content-Security-Policy ShouldNotContainSubstring "unsafe" + - name: X-Permitted-Cross-Domain-Policies + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.X-Permitted-Cross-Domain-Policies ShouldNotBeNil + - result.headers.X-Permitted-Cross-Domain-Policies ShouldEqual "none" + - name: Referrer-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Referrer-Policy ShouldNotBeNil + - result.headers.Referrer-Policy ShouldEqual "no-referrer" + - name: Clear-Site-Data + steps: + - type: http + method: GET + url: '{{.target_site}}/{{.logout_url}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Clear-Site-Data ShouldNotBeNil + - result.headers.Clear-Site-Data ShouldEqual '"cache","cookies","storage"' + - name: Cross-Origin-Embedder-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Cross-Origin-Embedder-Policy ShouldNotBeNil + - result.headers.Cross-Origin-Embedder-Policy ShouldEqual "require-corp" + - name: Cross-Origin-Opener-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Cross-Origin-Opener-Policy ShouldNotBeNil + - result.headers.Cross-Origin-Opener-Policy ShouldEqual "same-origin" + - name: Cross-Origin-Resource-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Cross-Origin-Resource-Policy ShouldNotBeNil + - result.headers.Cross-Origin-Resource-Policy ShouldEqual "same-origin" + - name: Permissions-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Permissions-Policy ShouldNotBeNil + - result.headers.Permissions-Policy ShouldEqual "accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), unload=()" + - name: Cache-Control + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Cache-Control ShouldNotBeNil + - 'result.headers.Cache-Control ShouldEqual "no-store, max-age=0"' + - name: Feature-Policy + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + info: >- + This header has now been renamed to Permissions-Policy in the + specification. + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Feature-Policy ShouldBeNil + - name: Public-Key-Pins + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + info: >- + This header has been deprecated by all major browsers and is no longer + recommended. Avoid using it, and update existing code if possible! + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Public-Key-Pins ShouldBeNil + - name: Expect-CT + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + info: >- + This header will likely become obsolete in June 2021. Since May 2018 + new certificates are expected to support SCTs by default. Certificates + before March 2018 were allowed to have a lifetime of 39 months, those + will all be expired in June 2021. + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.Expect-CT ShouldBeNil + - name: X-XSS-Protection + steps: + - type: http + method: GET + url: '{{.target_site}}' + skip_body: true + info: >- + The X-XSS-Protection header has been deprecated by modern browsers and + its use can introduce additional security issues on the client side. + timeout: '{{.request_timeout_in_seconds}}' + assertions: + - result.statuscode ShouldEqual 200 + - result.headers.X-XSS-Protection ShouldBeNil diff --git a/scripts/scancodeaudit.ps1 b/scripts/scancodeaudit.ps1 new file mode 100644 index 0000000..414a49d --- /dev/null +++ b/scripts/scancodeaudit.ps1 @@ -0,0 +1,9 @@ +param( + [Parameter(Mandatory = $true)] + [string]$jsonPath +) + +$scancodeJson = Get-Content $jsonPath | ConvertFrom-Json +$ReturnArray = @() +$scancodeJson.license_detections | Select-Object -ExpandProperty license_expression_spdx | ForEach-Object { $ReturnArray += $_ } +$ReturnArray | Sort-Object | Get-Unique diff --git a/scripts/tlsaudit.ps1 b/scripts/tlsaudit.ps1 new file mode 100644 index 0000000..ae323b6 --- /dev/null +++ b/scripts/tlsaudit.ps1 @@ -0,0 +1,15 @@ +param( + [Parameter(Mandatory = $true)] + [string]$domain, + [Parameter(Mandatory = $true)] + [string]$port +) + +$nmapResult = nmap -sV --script ssl-enum-ciphers -p $port $domain +$ciphers = $nmapResult | Select-String "TLS_" | ForEach-Object { $_[0].Line.Substring(8) } +$gradeRow = ($nmapResult | Select-String "least strength:")[0][0].Line +$grade = $gradeRow.Substring($gradeRow.Length - 1, 1) +$result = @() +$result += $grade +$result += $ciphers +$result diff --git a/unconfigure-githooks.ps1 b/unconfigure-githooks.ps1 new file mode 100644 index 0000000..e887e54 --- /dev/null +++ b/unconfigure-githooks.ps1 @@ -0,0 +1,5 @@ +[CmdletBinding()] +param() + +# Config git hooks +git config core.hooksPath .git/hooks