Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 30 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,25 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup .NET 8 SDK
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: '8.0.x'

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2

- name: Setup NuGet
uses: nuget/setup-nuget@v2
uses: nuget/setup-nuget@d105a947828025cd7a980103c35ba2bfae586d0f # v2

- name: Build all projects
shell: pwsh
run: ./pipeline-scripts/build.ps1
run: ./build/build.ps1

- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: build-output
path: |
Expand All @@ -60,31 +60,31 @@ jobs:
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup .NET 8 SDK
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: '8.0.x'

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2

- name: Download build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: build-output

- name: Install dotnet-coverage
shell: pwsh
run: dotnet tool install --global dotnet-coverage
run: dotnet tool install --global dotnet-coverage --version 18.5.2

- name: Run tests with coverage
shell: pwsh
run: ./pipeline-scripts/run-tests.ps1
run: ./build/run-tests.ps1

- name: Upload test results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: test-results
Expand All @@ -96,7 +96,7 @@ jobs:
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./pipeline-scripts/post-coverage-comment.ps1
run: ./build/post-coverage-comment.ps1

- name: Publish test results to job summary
if: always()
Expand Down Expand Up @@ -152,19 +152,19 @@ jobs:
needs: build
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.x'

- name: Run Semgrep security scan
shell: pwsh
run: ./pipeline-scripts/run-semgrep.ps1
run: ./build/run-semgrep.ps1

- name: Upload Semgrep results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: semgrep-results
Expand All @@ -184,7 +184,7 @@ jobs:
needs.security-scan.result == 'success'
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

Expand All @@ -209,35 +209,35 @@ jobs:

- name: Setup .NET 8 SDK
if: steps.changes.outputs.skip != 'true'
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: '8.0.x'

- name: Setup MSBuild
if: steps.changes.outputs.skip != 'true'
uses: microsoft/setup-msbuild@v2
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2

- name: Setup NuGet
if: steps.changes.outputs.skip != 'true'
uses: nuget/setup-nuget@v2
uses: nuget/setup-nuget@d105a947828025cd7a980103c35ba2bfae586d0f # v2

- name: Setup Java (required by SonarScanner)
if: steps.changes.outputs.skip != 'true'
uses: actions/setup-java@v4
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
distribution: 'temurin'
java-version: '17'

- name: Download test results
if: steps.changes.outputs.skip != 'true'
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: test-results
path: test-results

- name: Download Semgrep results
if: steps.changes.outputs.skip != 'true'
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: semgrep-results
path: semgrep-results
Expand All @@ -249,7 +249,7 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_PROJECT_KEY: ${{ secrets.SONAR_PROJECT_KEY }}
SONAR_ORGANIZATION: ${{ secrets.SONAR_ORGANIZATION }}
run: ./pipeline-scripts/upload-to-sonarcloud.ps1
run: ./build/upload-to-sonarcloud.ps1

# ==========================================================================
# Job 5: Quality Gate Check
Expand All @@ -261,20 +261,20 @@ jobs:
if: always()
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Download test results
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: test-results
path: test-results

- name: Download Semgrep results
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: semgrep-results
path: semgrep-results

- name: Check quality gates
shell: pwsh
run: ./pipeline-scripts/check-quality-gates.ps1
run: ./build/check-quality-gates.ps1
46 changes: 46 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

SDLC reference implementation for Puma Security. Demonstrates CI/CD pipelines (GitHub Actions on `windows-latest`) across three .NET project types with automated testing, code coverage, Semgrep security scanning, and SonarCloud quality analysis.

## Build & Test Commands

The SDK-style projects (.NET 8 and .NET Framework SDK) can be built and tested locally with `dotnet`. The traditional .NET Framework project requires MSBuild/Visual Studio (Windows only).

```shell
# Build SDK-style projects
dotnet build src/PumaSecurity.SDLC.Web.Net/PumaSecurity.SDLC.Web.Net.csproj
dotnet build src/PumaSecurity.SDLC.Web.NetFrameworkSdk/PumaSecurity.SDLC.Web.NetFrameworkSdk.csproj

# Run .NET 8 tests
dotnet test tests/PumaSecurity.SDLC.Web.Net.Tests/PumaSecurity.SDLC.Web.Net.Tests.csproj

# Run .NET Framework SDK tests (Windows only, requires net472 targeting pack)
dotnet test tests/PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests/PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests.csproj
```

The `build/` directory contains PowerShell scripts used by CI, designed for Windows runners.

## Architecture

Three parallel project types demonstrate different .NET build configurations:

| Project | Target | Build Tool | Style |
|---------|--------|------------|-------|
| `PumaSecurity.SDLC.Web.NetFramework` | .NET Framework 4.7.2 | MSBuild | Traditional (non-SDK) csproj, `packages.config` |
| `PumaSecurity.SDLC.Web.NetFrameworkSdk` | .NET Framework 4.7.2 | `dotnet build` | SDK-style csproj, `PackageReference` |
| `PumaSecurity.SDLC.Web.Net` | .NET 8.0 | `dotnet build` | SDK-style csproj, nullable enabled |

Each source project in `src/` has a corresponding MSTest test project in `tests/`. Test framework is MSTest with `JUnitXml.TestLogger` for CI report generation.

## CI Pipeline (`.github/workflows/ci.yml`)

Five jobs: Build → Test & Security Scan (parallel) → SonarCloud Analysis → Quality Gate Check. Quality gates enforce: all tests pass, ≥70% code coverage, zero critical Semgrep findings.

## Intentional Vulnerabilities

`src/PumaSecurity.SDLC.Web.Net/UserService.cs` contains deliberate security flaws (SQL injection, hardcoded credentials, MD5, command injection) for testing Semgrep detection. Do not "fix" these unless asked.

12 changes: 6 additions & 6 deletions PumaSecurity.SDLC.Web.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36511.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFramework", "PumaSecurity.SDLC.Web.NetFramework\PumaSecurity.SDLC.Web.NetFramework.csproj", "{FF517134-C558-40D5-ADD8-90A14A73F120}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFramework", "src\PumaSecurity.SDLC.Web.NetFramework\PumaSecurity.SDLC.Web.NetFramework.csproj", "{FF517134-C558-40D5-ADD8-90A14A73F120}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
.github\workflows\ci.yml = .github\workflows\ci.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFramework.Tests", "PumaSecurity.SDLC.Web.NetFramework.Tests\PumaSecurity.SDLC.Web.NetFramework.Tests.csproj", "{E1FEDDC1-9397-4813-8E84-8732D26E0591}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFramework.Tests", "tests\PumaSecurity.SDLC.Web.NetFramework.Tests\PumaSecurity.SDLC.Web.NetFramework.Tests.csproj", "{E1FEDDC1-9397-4813-8E84-8732D26E0591}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFrameworkSdk", "PumaSecurity.SDLC.Web.NetFrameworkSdk\PumaSecurity.SDLC.Web.NetFrameworkSdk.csproj", "{867E86E5-8256-416D-85D6-C69DAD2604DB}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFrameworkSdk", "src\PumaSecurity.SDLC.Web.NetFrameworkSdk\PumaSecurity.SDLC.Web.NetFrameworkSdk.csproj", "{867E86E5-8256-416D-85D6-C69DAD2604DB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests", "PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests\PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests.csproj", "{A6EA189E-6118-4AB9-BBF3-DCA5E789F4A5}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests", "tests\PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests\PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests.csproj", "{A6EA189E-6118-4AB9-BBF3-DCA5E789F4A5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.Net", "PumaSecurity.SDLC.Web.Net\PumaSecurity.SDLC.Web.Net.csproj", "{A8F5267D-7DC5-4AC9-8590-DEE23249AC17}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.Net", "src\PumaSecurity.SDLC.Web.Net\PumaSecurity.SDLC.Web.Net.csproj", "{A8F5267D-7DC5-4AC9-8590-DEE23249AC17}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.Net.Tests", "PumaSecurity.SDLC.Web.Net.Tests\PumaSecurity.SDLC.Web.Net.Tests.csproj", "{C6B401D7-8D11-4C7A-9C7A-C43DF7589404}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PumaSecurity.SDLC.Web.Net.Tests", "tests\PumaSecurity.SDLC.Web.Net.Tests\PumaSecurity.SDLC.Web.Net.Tests.csproj", "{C6B401D7-8D11-4C7A-9C7A-C43DF7589404}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
39 changes: 20 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ sdlc-reference-implementation/
├── .github/workflows/ci.yml # CI/CD pipeline configuration
├── PumaSecurity.SDLC.Web.sln # Solution file
├── pipeline-scripts/ # All pipeline automation scripts
├── build/ # All pipeline automation scripts
│ ├── build.ps1 # Build all projects
│ ├── run-tests.ps1 # Execute tests with coverage
│ ├── run-semgrep.ps1 # Semgrep security scanning
Expand All @@ -85,14 +85,15 @@ sdlc-reference-implementation/
│ ├── post-coverage-comment.ps1 # Post coverage to PR comments
│ └── coverage.runsettings # Coverage configuration
├── PumaSecurity.SDLC.Web.NetFramework/ # Traditional .NET Framework MVC app
├── PumaSecurity.SDLC.Web.NetFramework.Tests/ # Tests for traditional project
├── src/
│ ├── PumaSecurity.SDLC.Web.NetFramework/ # Traditional .NET Framework MVC app
│ ├── PumaSecurity.SDLC.Web.NetFrameworkSdk/ # SDK-style .NET Framework class library
│ └── PumaSecurity.SDLC.Web.Net/ # Modern .NET 8 class library
├── PumaSecurity.SDLC.Web.NetFrameworkSdk/ # SDK-style .NET Framework class library
├── PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests/ # Tests for SDK-style Framework
├── PumaSecurity.SDLC.Web.Net/ # Modern .NET 8 class library
├── PumaSecurity.SDLC.Web.Net.Tests/ # Tests for modern .NET
├── tests/
│ ├── PumaSecurity.SDLC.Web.NetFramework.Tests/ # Tests for traditional project
│ ├── PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests/ # Tests for SDK-style Framework
│ └── PumaSecurity.SDLC.Web.Net.Tests/ # Tests for modern .NET
├── test-results/ # Generated test & coverage output
└── semgrep-results/ # Generated Semgrep scan output
Expand All @@ -102,22 +103,22 @@ sdlc-reference-implementation/

### Build All Projects
```powershell
.\pipeline-scripts\build.ps1
.\build\build.ps1
```

### Run All Tests with Coverage
```powershell
.\pipeline-scripts\run-tests.ps1
.\build\run-tests.ps1
```

### Run Security Scan
```powershell
.\pipeline-scripts\run-semgrep.ps1
.\build\run-semgrep.ps1
```

### Show Coverage Report
```powershell
.\pipeline-scripts\show-coverage.ps1
.\build\show-coverage.ps1
```

## Pipeline Scripts
Expand Down Expand Up @@ -188,7 +189,7 @@ Semgrep OSS scans the codebase for security vulnerabilities:
- `semgrep-results/semgrep.sarif` — SARIF format for SonarCloud import (post-processed for compatibility)

### Intentional Test Vulnerabilities
`PumaSecurity.SDLC.Web.Net/UserService.cs` contains intentional vulnerabilities for testing Semgrep detection:
`src/PumaSecurity.SDLC.Web.Net/UserService.cs` contains intentional vulnerabilities for testing Semgrep detection:
- SQL injection (string concatenation in SQL command)
- Hardcoded credentials
- Weak cryptography (MD5)
Expand Down Expand Up @@ -241,25 +242,25 @@ python -m pip install semgrep
### Build Individual Projects
```powershell
# Traditional .NET Framework MVC
msbuild PumaSecurity.SDLC.Web.NetFramework\PumaSecurity.SDLC.Web.NetFramework.csproj /p:Configuration=Release
msbuild src\PumaSecurity.SDLC.Web.NetFramework\PumaSecurity.SDLC.Web.NetFramework.csproj /p:Configuration=Release

# SDK-style .NET Framework
dotnet build PumaSecurity.SDLC.Web.NetFrameworkSdk\PumaSecurity.SDLC.Web.NetFrameworkSdk.csproj --configuration Release
dotnet build src\PumaSecurity.SDLC.Web.NetFrameworkSdk\PumaSecurity.SDLC.Web.NetFrameworkSdk.csproj --configuration Release

# Modern .NET 8
dotnet build PumaSecurity.SDLC.Web.Net\PumaSecurity.SDLC.Web.Net.csproj --configuration Release
dotnet build src\PumaSecurity.SDLC.Web.Net\PumaSecurity.SDLC.Web.Net.csproj --configuration Release
```

### Run Individual Test Projects
```powershell
# Traditional Framework tests (VSTest)
vstest.console.exe PumaSecurity.SDLC.Web.NetFramework.Tests\bin\Release\net472\PumaSecurity.SDLC.Web.NetFramework.Tests.dll
vstest.console.exe tests\PumaSecurity.SDLC.Web.NetFramework.Tests\bin\Release\net472\PumaSecurity.SDLC.Web.NetFramework.Tests.dll

# SDK-style Framework tests (VSTest)
vstest.console.exe PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests\bin\Release\net472\PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests.dll
vstest.console.exe tests\PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests\bin\Release\net472\PumaSecurity.SDLC.Web.NetFrameworkSdk.Tests.dll

# Modern .NET 8 tests (dotnet test)
dotnet test PumaSecurity.SDLC.Web.Net.Tests\PumaSecurity.SDLC.Web.Net.Tests.csproj --configuration Release
dotnet test tests\PumaSecurity.SDLC.Web.Net.Tests\PumaSecurity.SDLC.Web.Net.Tests.csproj --configuration Release
```

## Troubleshooting
Expand Down
Loading