diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..b751de6 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,340 @@ +# CI/CD Workflows Documentation + +This directory contains GitHub Actions workflows for continuous integration and continuous deployment. + +## Workflows Overview + +### 🔧 Main CI Workflow (`ci.yml`) + +The primary continuous integration workflow for Rust projects. + +**Triggers:** +- Push to `master`, `main`, `develop` branches +- Pull requests to `master`, `main`, `develop` branches + +**Jobs:** +- **Format Check** (`fmt`): Runs `cargo fmt` with auto-fix and commit +- **Linting** (`clippy`): Runs `cargo clippy` on backend and frontend +- **Build** (`build`): Multi-architecture builds (x86_64, aarch64, GNU/musl) + - Builds backend with `cargo-auditable` for security + - Builds frontend WASM with Trunk (x86_64 only) + - Optional UPX compression (controlled by `.github/upx/` flags) +- **Test** (`test`): Runs unit tests, integration tests, and doc tests +- **Benchmark** (`bench`): Compiles benchmarks to ensure they build +- **Security Audit** (`audit`): Uses `cargo-audit` to check for vulnerabilities +- **Dependency Policy** (`deny`): Uses `cargo-deny` to enforce dependency policies + +**Features:** +- Granular skip control via `.github/skips/` flag files +- Rust file change detection (only runs when Rust files change) +- Multi-architecture build matrix +- Frontend/Backend support with separate checks +- Build artifacts uploaded for each architecture + +--- + +### 📊 Coverage Workflow (`ci-coverage.yml`) + +Generates code coverage reports and uploads to Codecov. + +**Triggers:** +- Push to `master`, `main`, `develop` branches +- Pull requests to `master`, `main`, `develop` branches + +**Jobs:** +- Format, Clippy, Test, Benchmark (same as main CI) +- **Coverage** (`coverage`): Generates coverage with `cargo-tarpaulin` + - Uploads to Codecov (requires `CODECOV_TOKEN` secret) + - Archives coverage report as artifact + +**Requirements:** +- Set `CODECOV_TOKEN` in repository secrets for coverage upload + +--- + +### 🔒 Security Scan Workflow (`security-scan.yml`) + +Comprehensive security scanning across multiple layers. + +**Triggers:** +- Push to `master`, `main`, `develop` branches +- Pull requests to `master`, `main`, `develop` branches +- Daily schedule at 2 AM UTC +- Manual trigger (`workflow_dispatch`) + +**Jobs:** + +#### Skippable Security Jobs +- **Dependency Review** (`dependency-review`): Reviews dependencies in PRs +- **Rust Security** (`rust-security`): `cargo-audit` + `cargo-deny` +- **NPM Security** (`npm-security`): NPM audit + Snyk (if `package.json` exists) +- **Python Security** (`python-security`): Safety + Bandit (if Python files exist) +- **CodeQL** (`codeql`): Static analysis with GitHub CodeQL +- **Container Scan** (`container-scan`): Trivy scan (if `Dockerfile` exists) +- **License Check** (`license-check`): Validates dependency licenses + +#### CRITICAL: Always-Run Secret Scanning +These jobs **CANNOT be skipped** and protect against accidental credential commits: +- **git-secrets**: Detects AWS keys, API credentials, tokens, private keys +- **detect-secrets**: Creates baseline and identifies credential exposure +- **TruffleHog**: Scans repository history for verified secrets + +**Important:** +- Secret scanning jobs run on **every push** regardless of skip flags +- Failures indicate potential credential leaks - investigate immediately + +--- + +### 🤖 AI Bot Mentions Workflow (`ai-bot-mentions.yml`) + +Template for AI-powered bot integration in issues and PRs. + +**Triggers:** +- Issue comments created +- Issues opened/edited +- Pull requests opened/edited +- PR review comments created + +**Features:** +- Detects `@ai-bot` mentions in comments and issue/PR bodies +- Extracts command after mention +- Posts acknowledgment comment +- Template for AI service integration (Claude, ChatGPT, custom APIs) +- Error handling with user feedback + +**Configuration:** +- Edit `env.BOT_MENTION` to change trigger phrase +- Implement AI integration in `Call AI Service` step +- Add API keys as repository secrets + +--- + +### 🚀 Release Workflow (`release.yml`) + +Builds release binaries with optimizations. + +**Triggers:** +- Git tags matching `v*` (e.g., `v1.0.0`) +- Manual trigger (`workflow_dispatch`) + +**Jobs:** +- Builds optimized release binaries for multiple targets +- Builds frontend WASM bundle +- Compresses with UPX for smaller artifacts +- Creates GitHub release with attached binaries + +--- + +## Skip Control System + +Control CI execution with flag files in `.github/skips/`: + +### Global Skip +- `.skip-ci` - Skips ALL CI jobs (except critical security scans) + +### Individual Job Skips +- `.skip-fmt` / `.skip-format` - Skip formatting +- `.skip-clippy` / `.skip-lint` - Skip linting +- `.skip-build` - Skip builds +- `.skip-test` / `.skip-tests` - Skip tests +- `.skip-bench` / `.skip-benchmark` - Skip benchmarks +- `.skip-audit` / `.skip-security` - Skip security audits +- `.skip-deny` - Skip cargo-deny +- `.skip-ai-bot` / `.skip-bot` - Skip AI bot workflow + +### Usage + +**Local skip (not committed):** +```bash +touch .github/skips/.skip-test +# Run CI - tests will be skipped locally +# File is gitignored, won't affect others +``` + +**Repository skip (committed):** +```bash +touch .github/skips/.skip-test +git add .github/skips/.skip-test +git commit -m "ci: skip tests temporarily" +git push +# CI skips tests for everyone until removed +``` + +See `.github/skips/README.md` for detailed documentation. + +--- + +## UPX Compression + +Enable binary compression with flag files in `.github/upx/`: + +- `.enable-upx` - Enable for ALL architectures +- `.enable-upx-x86_64` - Enable for x86_64 only +- `.enable-upx-arm` - Enable for ARM64/aarch64 only + +**Example:** +```bash +touch .github/upx/.enable-upx-x86_64 +git add .github/upx/.enable-upx-x86_64 +git commit -m "build: enable UPX compression for x86_64" +git push +``` + +See `.github/upx/README.md` for more information. + +--- + +## Architecture Overview + +``` +.github/ +├── workflows/ +│ ├── ci.yml # Main CI workflow +│ ├── ci-coverage.yml # Coverage reporting +│ ├── security-scan.yml # Security scanning +│ ├── ai-bot-mentions.yml # AI bot integration +│ └── release.yml # Release builds +├── skips/ +│ ├── .gitkeep +│ ├── README.md # Skip control documentation +│ └── .skip-* (gitignored) # Skip flag files +├── upx/ +│ ├── .gitkeep +│ ├── README.md # UPX documentation +│ └── .enable-upx* (gitignored) # UPX flag files +└── security/ + ├── .gitkeep + ├── README.md # Security configuration + └── secrets-patterns.txt # Custom secret patterns (optional) +``` + +--- + +## Multi-Architecture Support + +The CI builds for the following targets: +- `x86_64-unknown-linux-gnu` - Standard Linux (glibc) +- `x86_64-unknown-linux-musl` - Static Linux (musl) +- `aarch64-unknown-linux-gnu` - ARM64 Linux (glibc) +- `aarch64-unknown-linux-musl` - ARM64 Linux (musl) + +Frontend WASM builds only on `x86_64-unknown-linux-gnu` to save CI time. + +--- + +## Frontend Integration + +The CI has special handling for the frontend (Yew/WASM): + +### Format Check +- Backend: `cargo fmt --all` +- Frontend: `cd frontend && cargo fmt --all` + +### Linting +- Backend: `cargo clippy --all-targets --all-features` +- Frontend: `cd frontend && cargo clippy --all-targets --all-features` + +### Build +- Backend: Multi-arch with `cargo-auditable` / `cross` +- Frontend: `trunk build --release` (x86_64 only) + - Installs Trunk automatically + - Adds `wasm32-unknown-unknown` target + - Outputs to `frontend/dist/` + +### Test +- Backend: `cargo test --all-features --all` +- Frontend: `cd frontend && cargo test --all-features` + +--- + +## Required Secrets + +Configure these in repository settings: + +- `CODECOV_TOKEN` - For coverage upload (ci-coverage.yml) +- `SNYK_TOKEN` - For Snyk scanning (security-scan.yml, optional) + +--- + +## Best Practices + +1. **Use skip flags sparingly** - Only for emergencies or known failures +2. **Never skip security scans** - They protect against credential leaks +3. **Document skip reasons** - Always explain why in commit messages +4. **Remove skip flags ASAP** - Don't leave them committed longer than needed +5. **Test locally first** - Use `cargo test`, `cargo clippy` before pushing +6. **Enable UPX for releases** - Keep disabled during development +7. **Review security scan results** - Daily scans catch new vulnerabilities + +--- + +## Troubleshooting + +### CI is not running +- Check for `.github/skips/.skip-ci` file +- Verify branch name matches trigger branches +- Check if only non-code files changed (e.g., only `.md` files) + +### Formatting job fails +- Run `cargo fmt --all` locally +- Run `cd frontend && cargo fmt --all` locally +- Commit the formatted code + +### Clippy job fails +- Run `cargo clippy --all-targets --all-features` locally +- Fix warnings or add `#[allow(clippy::...)]` if intentional +- Frontend: `cd frontend && cargo clippy --all-targets --all-features` + +### Build job fails +- Check if all dependencies are in `Cargo.toml` +- For cross-compilation issues, test with `cross` locally +- Verify frontend builds: `cd frontend && trunk build` + +### Secret scanning fails +- Review the detected pattern +- If false positive, add to `.secrets.baseline` and audit +- If real secret, rotate immediately and update repository + +### Coverage upload fails +- Verify `CODECOV_TOKEN` is set in repository secrets +- Check Codecov service status +- Review workflow logs for specific error + +--- + +## Integration with ryugen-io/.ci-workflows + +These workflows are based on the standard templates from [ryugen-io/.ci-workflows](https://github.com/ryugen-io/.ci-workflows): + +- `rust/standard/ci.yml` → `ci.yml` (with frontend integration) +- `rust/coverage/ci-coverage.yml` → `ci-coverage.yml` (with frontend tests) +- `general/security/security-scan.yml` → `security-scan.yml` +- `general/bots/ai-bot-mentions.yml` → `ai-bot-mentions.yml` + +**Customizations:** +- Added frontend/WASM build support (Trunk) +- Separate backend/frontend format and clippy checks +- Frontend tests integrated into test job +- Frontend artifacts uploaded alongside backend + +--- + +## Maintenance + +To update workflows from upstream: + +```bash +# Fetch latest templates +curl -O https://raw.githubusercontent.com/ryugen-io/.ci-workflows/main/rust/standard/ci.yml + +# Review changes +diff ci.yml .github/workflows/ci.yml + +# Merge updates manually, preserving frontend customizations +``` + +**Important:** Always preserve frontend-specific steps when merging updates! + +--- + +For questions or issues, please refer to the [.ci-workflows documentation](https://github.com/ryugen-io/.ci-workflows). diff --git a/.github/security/.gitkeep b/.github/security/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.github/security/README.md b/.github/security/README.md new file mode 100644 index 0000000..681dce1 --- /dev/null +++ b/.github/security/README.md @@ -0,0 +1,47 @@ +# Security Configuration + +This directory contains security-related configuration files for CI workflows. + +## Files + +### `secrets-patterns.txt` (Optional) + +Add custom secret patterns for git-secrets to detect: + +``` +# Example patterns +custom-api-key-[0-9a-zA-Z]{32} +MY_COMPANY_SECRET_[A-Z0-9]+ +``` + +## Secret Scanning + +The security workflow includes three layers of secret scanning: + +1. **git-secrets**: Scans for AWS credentials and custom patterns +2. **detect-secrets**: Creates a baseline and detects new secrets +3. **TruffleHog**: Scans repository history for verified secrets + +All three secret scanning jobs **ALWAYS run** and cannot be skipped. + +## Adding Custom Patterns + +Create a `secrets-patterns.txt` file in this directory: + +```bash +cat > .github/security/secrets-patterns.txt << 'EOF' +# Custom API keys +custom-api-[0-9a-zA-Z]{40} + +# Internal tokens +INTERNAL_TOKEN_[A-Z0-9]{32} +EOF +``` + +Then commit: + +```bash +git add .github/security/secrets-patterns.txt +git commit -m "security: add custom secret patterns" +git push +``` diff --git a/.github/skips/.gitkeep b/.github/skips/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.github/skips/README.md b/.github/skips/README.md new file mode 100644 index 0000000..e1b54e4 --- /dev/null +++ b/.github/skips/README.md @@ -0,0 +1,73 @@ +# CI Skip Files + +This directory contains flag files to control CI/CD workflow execution. + +## How It Works + +The presence of specific files in this directory will skip certain CI jobs: + +### Global Skip + +- `.skip-ci` - Skips **ALL** CI jobs (except critical security scans) + +### Individual Job Skips + +- `.skip-fmt` or `.skip-format` - Skip code formatting checks +- `.skip-clippy` or `.skip-lint` - Skip linting (clippy) checks +- `.skip-build` - Skip build jobs +- `.skip-test` or `.skip-tests` - Skip test execution +- `.skip-bench` or `.skip-benchmark` - Skip benchmark compilation checks +- `.skip-audit` or `.skip-security` - Skip security audit jobs +- `.skip-deny` - Skip cargo-deny dependency policy checks +- `.skip-ai-bot` or `.skip-bot` - Skip AI bot mention workflow + +## Usage Examples + +### Skip all CI temporarily + +```bash +touch .github/skips/.skip-ci +git add .github/skips/.skip-ci +git commit -m "ci: temporarily disable CI" +git push +``` + +### Skip only tests during development + +```bash +touch .github/skips/.skip-test +git add .github/skips/.skip-test +git commit -m "ci: skip tests temporarily" +git push +``` + +### Re-enable all CI + +```bash +rm .github/skips/.skip-ci +git add .github/skips/.skip-ci +git commit -m "ci: re-enable CI" +git push +``` + +## Important Notes + +1. **Secret Scanning ALWAYS Runs**: The following security jobs cannot be skipped: + - `git-secrets` + - `detect-secrets` + - `trufflehog-scan` + +2. **Skip Files Must Be Committed**: To skip CI on GitHub Actions, you MUST commit the skip files to the repository. They are NOT gitignored. + +## Best Practices + +- Use global `.skip-ci` sparingly - only for emergencies or major repository changes +- Prefer individual job skips when you know specific jobs will fail +- **Always commit skip files** - they must be in the repository to work on GitHub Actions +- Always document why you're skipping CI in your commit message +- Remove skip files as soon as the issue is resolved +- Never skip security scans unless absolutely necessary and you understand the risks + +## Note + +Skip files are **NOT gitignored** and will be tracked in the repository when committed. This is intentional - CI runs on GitHub Actions and needs to see these files. diff --git a/.github/upx/.gitkeep b/.github/upx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.github/upx/README.md b/.github/upx/README.md new file mode 100644 index 0000000..729cbb7 --- /dev/null +++ b/.github/upx/README.md @@ -0,0 +1,71 @@ +# UPX Compression Flags + +This directory contains flag files to enable UPX (Ultimate Packer for eXecutables) compression for binaries. + +## How It Works + +The presence of specific files in this directory will enable UPX compression during the build process: + +### Compression Flags + +- `.enable-upx` - Enable UPX compression for **ALL** architectures +- `.enable-upx-x86_64` - Enable UPX compression for x86_64 architectures only +- `.enable-upx-arm` - Enable UPX compression for ARM64/aarch64 architectures only + +## Usage Examples + +### Enable UPX for all architectures + +```bash +touch .github/upx/.enable-upx +git add .github/upx/.enable-upx +git commit -m "build: enable UPX compression for all architectures" +git push +``` + +### Enable UPX only for x86_64 + +```bash +touch .github/upx/.enable-upx-x86_64 +git add .github/upx/.enable-upx-x86_64 +git commit -m "build: enable UPX compression for x86_64 only" +git push +``` + +### Disable UPX compression + +```bash +rm .github/upx/.enable-upx* +git add .github/upx/ +git commit -m "build: disable UPX compression" +git push +``` + +## What is UPX? + +UPX is a free, portable, extendable, high-performance executable packer. It compresses executables to reduce their size significantly. + +### Advantages + +- **Smaller binaries**: Can reduce binary size by 50-70% +- **Faster downloads**: Smaller artifacts mean faster downloads +- **No runtime dependencies**: Decompression happens automatically at runtime + +### Disadvantages + +- **Slower startup**: Binaries need to decompress at runtime (milliseconds) +- **Antivirus false positives**: Some antivirus software may flag packed binaries +- **Debug limitations**: Packed binaries are harder to debug + +## Important Notes + +1. UPX compression is **disabled by default** +2. Enable only if you need smaller binary sizes (e.g., for releases) +3. Test thoroughly - UPX can occasionally cause issues with some binaries +4. The CI uses `--best --lzma` for maximum compression + +## Recommended Usage + +- **Development**: Keep UPX disabled +- **Testing**: Keep UPX disabled +- **Production/Releases**: Enable UPX for specific architectures as needed diff --git a/.github/workflows/ai-bot-mentions.yml b/.github/workflows/ai-bot-mentions.yml new file mode 100644 index 0000000..6d0b6fb --- /dev/null +++ b/.github/workflows/ai-bot-mentions.yml @@ -0,0 +1,274 @@ +name: AI Bot Mentions + +# This workflow triggers when a bot is mentioned in issue comments or PR comments +# It can be used to integrate with AI services like Claude, ChatGPT, or custom bots + +on: + issue_comment: + types: [created] + issues: + types: [opened, edited] + pull_request: + types: [opened, edited] + pull_request_review_comment: + types: [created] + +# Workflow-level permissions +permissions: + issues: write + pull-requests: write + contents: read + +env: + # Configure your bot mention trigger + # Examples: "@ai-assistant", "@bot", "@claude", "@gpt" + BOT_MENTION: "@ai-bot" + + # Enable debug logging (set to 'true' for verbose output) + DEBUG: "false" + +jobs: + # Check if CI should be skipped + check-skip: + runs-on: ubuntu-latest + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + skip_ai_bot: ${{ steps.skip_check.outputs.skip_ai_bot }} + steps: + - uses: actions/checkout@v4 + + - name: Check for skip flags + id: skip_check + run: | + # Global skip - skips all CI + if [ -f .github/skips/.skip-ci ]; then + echo "should_skip=true" >> $GITHUB_OUTPUT + else + echo "should_skip=false" >> $GITHUB_OUTPUT + fi + + # AI bot specific skip flag + if [ -f .github/skips/.skip-ai-bot ] || [ -f .github/skips/.skip-bot ]; then + echo "skip_ai_bot=true" >> $GITHUB_OUTPUT + else + echo "skip_ai_bot=false" >> $GITHUB_OUTPUT + fi + + # Job to detect and process bot mentions + detect-mention: + needs: check-skip + name: Detect Bot Mention + runs-on: ubuntu-latest + + # Only run if not skipped and the comment/body contains the bot mention + if: | + needs.check-skip.outputs.should_skip != 'true' && + needs.check-skip.outputs.skip_ai_bot != 'true' && + ( + (github.event.comment.body && contains(github.event.comment.body, '@ai-bot')) || + (github.event.issue.body && contains(github.event.issue.body, '@ai-bot')) || + (github.event.pull_request.body && contains(github.event.pull_request.body, '@ai-bot')) + ) + + outputs: + should_process: ${{ steps.check.outputs.should_process }} + comment_body: ${{ steps.extract.outputs.comment_body }} + issue_number: ${{ steps.extract.outputs.issue_number }} + is_pr: ${{ steps.extract.outputs.is_pr }} + + steps: + - name: Check if bot mention exists + id: check + run: | + echo "Bot mention detected!" + echo "should_process=true" >> $GITHUB_OUTPUT + + - name: Extract comment details + id: extract + run: | + # Determine the source of the mention + if [ "${{ github.event_name }}" = "issue_comment" ]; then + BODY="${{ github.event.comment.body }}" + ISSUE_NUM="${{ github.event.issue.number }}" + IS_PR="${{ github.event.issue.pull_request != null }}" + elif [ "${{ github.event_name }}" = "issues" ]; then + BODY="${{ github.event.issue.body }}" + ISSUE_NUM="${{ github.event.issue.number }}" + IS_PR="false" + elif [ "${{ github.event_name }}" = "pull_request" ]; then + BODY="${{ github.event.pull_request.body }}" + ISSUE_NUM="${{ github.event.pull_request.number }}" + IS_PR="true" + elif [ "${{ github.event_name }}" = "pull_request_review_comment" ]; then + BODY="${{ github.event.comment.body }}" + ISSUE_NUM="${{ github.event.pull_request.number }}" + IS_PR="true" + fi + + # Output the extracted data + echo "comment_body<> $GITHUB_OUTPUT + echo "$BODY" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "issue_number=$ISSUE_NUM" >> $GITHUB_OUTPUT + echo "is_pr=$IS_PR" >> $GITHUB_OUTPUT + + # Debug output + if [ "${{ env.DEBUG }}" = "true" ]; then + echo "Event: ${{ github.event_name }}" + echo "Issue/PR: $ISSUE_NUM" + echo "Is PR: $IS_PR" + echo "Body: $BODY" + fi + + # Job to process the mention and respond + process-mention: + name: Process Bot Mention + needs: [check-skip, detect-mention] + runs-on: ubuntu-latest + if: | + needs.check-skip.outputs.should_skip != 'true' && + needs.check-skip.outputs.skip_ai_bot != 'true' && + needs.detect-mention.outputs.should_process == 'true' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Parse command from mention + id: parse + run: | + BODY="${{ needs.detect-mention.outputs.comment_body }}" + + # Extract the command after the bot mention + # Example: "@ai-bot help" -> command="help" + # Example: "@ai-bot analyze this code" -> command="analyze this code" + COMMAND=$(echo "$BODY" | grep -oP '@ai-bot\s+\K.*' | head -n1 || echo "") + + echo "command=$COMMAND" >> $GITHUB_OUTPUT + + echo "Parsed command: $COMMAND" + + - name: Acknowledge mention + uses: actions/github-script@v7 + with: + script: | + const issueNumber = ${{ needs.detect-mention.outputs.issue_number }}; + const isPR = ${{ needs.detect-mention.outputs.is_pr }}; + + // Post an acknowledgment comment + const comment = `👋 Hello! I detected your mention. Processing your request...\n\n` + + `**Command:** \`${{ steps.parse.outputs.command }}\`\n\n` + + `_This is an automated response. Configure your AI integration to process this request._`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: comment + }); + + # =================================================================== + # INTEGRATION POINT: Add your AI service integration here + # =================================================================== + # + # Examples of what you can do: + # 1. Call Claude API (Anthropic) + # 2. Call OpenAI API (ChatGPT) + # 3. Call a custom webhook/API endpoint + # 4. Trigger another workflow + # 5. Run code analysis tools + # 6. Generate responses based on repository context + # + # You'll typically want to: + # - Use secrets for API keys (e.g., secrets.ANTHROPIC_API_KEY) + # - Pass the command and context to your AI service + # - Post the AI's response back as a comment + # =================================================================== + + - name: Call AI Service (Example - Configure This!) + id: ai_service + env: + # Add your API keys as repository secrets + # Example: ANTHROPIC_API_KEY, OPENAI_API_KEY, etc. + # API_KEY: ${{ secrets.YOUR_API_KEY }} + COMMAND: ${{ steps.parse.outputs.command }} + ISSUE_NUMBER: ${{ needs.detect-mention.outputs.issue_number }} + IS_PR: ${{ needs.detect-mention.outputs.is_pr }} + run: | + # Example placeholder for AI service call + # Replace this with actual API call to your AI service + + echo "🤖 AI Service Integration Point" + echo "================================" + echo "Command: $COMMAND" + echo "Issue/PR: $ISSUE_NUMBER" + echo "Is PR: $IS_PR" + echo "" + echo "Configure this step to call your AI service!" + echo "Examples:" + echo " - Claude API: https://docs.anthropic.com/claude/reference" + echo " - OpenAI API: https://platform.openai.com/docs/api-reference" + echo " - Custom endpoint: curl -X POST your-api-endpoint" + + # Example response (replace with actual AI response) + RESPONSE="I'm a template bot! Configure the AI service integration to enable real responses. Available commands: help, analyze, review, explain, suggest" + + # Save response for next step + echo "response<> $GITHUB_OUTPUT + echo "$RESPONSE" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Post AI response + uses: actions/github-script@v7 + with: + script: | + const issueNumber = ${{ needs.detect-mention.outputs.issue_number }}; + const response = `${{ steps.ai_service.outputs.response }}`; + + // Post the AI's response as a comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: response + }); + + - name: Add reaction to original comment + if: github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' + uses: actions/github-script@v7 + with: + script: | + const commentId = ${{ github.event.comment.id }}; + + // Add a thumbs up reaction to show the bot processed the mention + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: commentId, + content: '+1' + }); + + # Optional: Job to handle errors + handle-error: + name: Handle Errors + needs: [check-skip, detect-mention, process-mention] + runs-on: ubuntu-latest + if: failure() + + steps: + - name: Post error message + uses: actions/github-script@v7 + with: + script: | + const issueNumber = ${{ needs.detect-mention.outputs.issue_number }}; + + const errorComment = `❌ Sorry, I encountered an error while processing your request.\n\n` + + `Please check the [workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId}) for details.`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: errorComment + }); diff --git a/.github/workflows/ci-coverage.yml b/.github/workflows/ci-coverage.yml new file mode 100644 index 0000000..1a01e00 --- /dev/null +++ b/.github/workflows/ci-coverage.yml @@ -0,0 +1,141 @@ +name: Rust CI with Coverage + +on: + push: + branches: [master, main, develop] + paths-ignore: + - '**.md' + - 'LICENSE*' + - 'docs/**' + pull_request: + branches: [master, main, develop] + paths-ignore: + - '**.md' + - 'LICENSE*' + - 'docs/**' + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + +jobs: + # Check if CI should be skipped + check-skip: + runs-on: ubuntu-latest + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - uses: actions/checkout@v4 + + - name: Check for .skip-ci file + id: skip_check + run: | + if [ -f .github/skips/.skip-ci ]; then + echo "should_skip=true" >> $GITHUB_OUTPUT + else + echo "should_skip=false" >> $GITHUB_OUTPUT + fi + + # Format check + fmt: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Check backend formatting + run: cargo fmt --all -- --check + + - name: Check frontend formatting + run: cd frontend && cargo fmt --all -- --check + + # Linting + clippy: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - uses: Swatinem/rust-cache@v2 + + - name: Run clippy on backend + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run clippy on frontend + run: cd frontend && cargo clippy --all-targets --all-features -- -D warnings + + # Test + test: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Run backend tests + run: cargo test --all-features --all + + - name: Run frontend tests + run: cd frontend && cargo test --all-features + + # Benchmark + bench: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Run benchmarks + run: cargo bench --all-features --no-run + + # Code coverage + coverage: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + + - name: Generate coverage for backend + run: cargo tarpaulin --all-features --workspace --timeout 300 --out xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./cobertura.xml + fail_ci_if_error: false + verbose: true + + - name: Archive coverage results + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: cobertura.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e76ef28..9a4818f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,135 +1,324 @@ -name: CI -# -# CI Control: Use .skip-ci file to disable CI -# -# To DISABLE CI: -# touch .skip-ci && git add .skip-ci && git commit -m "Disable CI" && git push -# -# To ENABLE CI: -# git rm .skip-ci && git commit -m "Enable CI" && git push -# -# Note: .skip-ci is gitignored by default for local development use -# +name: Rust CI on: push: - branches: [ master, main ] + branches: [master, main, develop] paths-ignore: - '**.md' - 'LICENSE*' - - '.gitignore' + - 'docs/**' pull_request: - branches: [ master, main ] + branches: [master, main, develop] paths-ignore: - '**.md' - 'LICENSE*' - - '.gitignore' + - 'docs/**' env: CARGO_TERM_COLOR: always - RUSTFLAGS: -D warnings RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings jobs: - # Check if CI should be skipped based on .skip-ci file + # Check if CI should be skipped check-skip: - name: Check Skip CI runs-on: ubuntu-latest outputs: - should_skip: ${{ steps.check.outputs.skip }} + should_skip: ${{ steps.skip_check.outputs.should_skip }} + has_rust_changes: ${{ steps.filter.outputs.rust }} + skip_fmt: ${{ steps.skip_check.outputs.skip_fmt }} + skip_clippy: ${{ steps.skip_check.outputs.skip_clippy }} + skip_build: ${{ steps.skip_check.outputs.skip_build }} + skip_test: ${{ steps.skip_check.outputs.skip_test }} + skip_bench: ${{ steps.skip_check.outputs.skip_bench }} + skip_audit: ${{ steps.skip_check.outputs.skip_audit }} + skip_deny: ${{ steps.skip_check.outputs.skip_deny }} steps: - uses: actions/checkout@v4 - - name: Check if CI should be skipped - id: check + + - name: Check for .skip-ci file + id: skip_check run: | - if [ -f .skip-ci ]; then - echo "⏭️ Found .skip-ci file - skipping all CI jobs" - echo "skip=true" >> $GITHUB_OUTPUT + # Global skip - skips all CI + if [ -f .github/skips/.skip-ci ]; then + echo "should_skip=true" >> $GITHUB_OUTPUT else - echo "✅ No .skip-ci file - running CI normally" - echo "skip=false" >> $GITHUB_OUTPUT + echo "should_skip=false" >> $GITHUB_OUTPUT fi + # Individual job skip flags + if [ -f .github/skips/.skip-fmt ] || [ -f .github/skips/.skip-format ]; then + echo "skip_fmt=true" >> $GITHUB_OUTPUT + else + echo "skip_fmt=false" >> $GITHUB_OUTPUT + fi + + if [ -f .github/skips/.skip-clippy ] || [ -f .github/skips/.skip-lint ]; then + echo "skip_clippy=true" >> $GITHUB_OUTPUT + else + echo "skip_clippy=false" >> $GITHUB_OUTPUT + fi + + if [ -f .github/skips/.skip-build ]; then + echo "skip_build=true" >> $GITHUB_OUTPUT + else + echo "skip_build=false" >> $GITHUB_OUTPUT + fi + + if [ -f .github/skips/.skip-test ] || [ -f .github/skips/.skip-tests ]; then + echo "skip_test=true" >> $GITHUB_OUTPUT + else + echo "skip_test=false" >> $GITHUB_OUTPUT + fi + + if [ -f .github/skips/.skip-bench ] || [ -f .github/skips/.skip-benchmark ]; then + echo "skip_bench=true" >> $GITHUB_OUTPUT + else + echo "skip_bench=false" >> $GITHUB_OUTPUT + fi + + if [ -f .github/skips/.skip-audit ] || [ -f .github/skips/.skip-security ]; then + echo "skip_audit=true" >> $GITHUB_OUTPUT + else + echo "skip_audit=false" >> $GITHUB_OUTPUT + fi + + if [ -f .github/skips/.skip-deny ]; then + echo "skip_deny=true" >> $GITHUB_OUTPUT + else + echo "skip_deny=false" >> $GITHUB_OUTPUT + fi + + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + rust: + - '**/*.rs' + - '**/Cargo.toml' + - '**/Cargo.lock' + + # Format check and auto-fix fmt: - needs: check-skip - if: needs.check-skip.outputs.should_skip != 'true' - name: Rustfmt runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_fmt != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' steps: - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - - name: Format backend + + - name: Check backend formatting run: cargo fmt --all -- --check - - name: Format frontend + continue-on-error: true + id: fmt_check_backend + + - name: Check frontend formatting run: cd frontend && cargo fmt --all -- --check + continue-on-error: true + id: fmt_check_frontend + + - name: Auto-format backend + if: steps.fmt_check_backend.outcome == 'failure' + run: cargo fmt --all + + - name: Auto-format frontend + if: steps.fmt_check_frontend.outcome == 'failure' + run: cd frontend && cargo fmt --all + - name: Commit formatting changes + if: steps.fmt_check_backend.outcome == 'failure' || steps.fmt_check_frontend.outcome == 'failure' + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add -A + git diff-index --quiet HEAD || git commit -m "chore: auto-format code with rustfmt" + git push + + # Linting clippy: - needs: check-skip - if: needs.check-skip.outputs.should_skip != 'true' - name: Clippy runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_clippy != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable with: components: clippy + - uses: Swatinem/rust-cache@v2 - - name: Clippy backend + + - name: Run clippy on backend run: cargo clippy --all-targets --all-features -- -D warnings - - name: Clippy frontend + + - name: Run clippy on frontend run: cd frontend && cargo clippy --all-targets --all-features -- -D warnings + # Build build: - needs: check-skip - if: needs.check-skip.outputs.should_skip != 'true' - name: Build runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_build != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 - - name: Build backend - run: cargo build --all-features - - name: Install trunk for frontend + with: + key: ${{ matrix.target }} + + - name: Install cargo-auditable + run: cargo install cargo-auditable + + - name: Install cross for non-native builds + if: matrix.target != 'x86_64-unknown-linux-gnu' + run: cargo install cross --git https://github.com/cross-rs/cross + + - name: Build backend with cargo-auditable + run: | + if [ "${{ matrix.target }}" = "x86_64-unknown-linux-gnu" ]; then + cargo auditable build --release --target ${{ matrix.target }} --all-features + else + cross auditable build --release --target ${{ matrix.target }} --all-features + fi + + - name: Install trunk for frontend (x86_64 only) + if: matrix.target == 'x86_64-unknown-linux-gnu' run: cargo install --locked trunk - - name: Build frontend (WASM) - run: cd frontend && trunk build + - name: Add wasm32 target (x86_64 only) + if: matrix.target == 'x86_64-unknown-linux-gnu' + run: rustup target add wasm32-unknown-unknown + + - name: Build frontend WASM (x86_64 only) + if: matrix.target == 'x86_64-unknown-linux-gnu' + run: cd frontend && trunk build --release + + - name: Check for UPX compression flag + id: upx_check + run: | + TARGET="${{ matrix.target }}" + if [ -f .github/upx/.enable-upx ]; then + # Global enable for all architectures + echo "enabled=true" >> $GITHUB_OUTPUT + elif [[ "$TARGET" == x86_64* ]] && [ -f .github/upx/.enable-upx-x86_64 ]; then + # x86_64 specific + echo "enabled=true" >> $GITHUB_OUTPUT + elif [[ "$TARGET" == aarch64* ]] && [ -f .github/upx/.enable-upx-arm ]; then + # ARM64 specific + echo "enabled=true" >> $GITHUB_OUTPUT + else + echo "enabled=false" >> $GITHUB_OUTPUT + fi + + - name: Install UPX + if: steps.upx_check.outputs.enabled == 'true' + run: | + sudo apt-get update + sudo apt-get install -y upx-ucl + + - name: Compress binaries with UPX + if: steps.upx_check.outputs.enabled == 'true' + run: | + for binary in target/${{ matrix.target }}/release/*; do + if [ -f "$binary" ] && [ -x "$binary" ] && file "$binary" | grep -q "ELF.*executable"; then + echo "Compressing $binary with UPX..." + upx --best --lzma "$binary" || echo "Failed to compress $binary, skipping..." + fi + done + + - name: Upload backend artifacts + uses: actions/upload-artifact@v4 + with: + name: backend-${{ matrix.target }} + path: target/${{ matrix.target }}/release/config-manager-server + + - name: Upload frontend artifacts (x86_64 only) + if: matrix.target == 'x86_64-unknown-linux-gnu' + uses: actions/upload-artifact@v4 + with: + name: frontend-wasm + path: frontend/dist/ + + # Test test: - needs: [check-skip, build] - if: needs.check-skip.outputs.should_skip != 'true' - name: Test runs-on: ubuntu-latest + needs: [check-skip, build] + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_test != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Run backend tests - run: cargo test --all-features + run: cargo test --all-features --all + - name: Run frontend tests run: cd frontend && cargo test --all-features + - name: Run doc tests - run: cargo test --doc --all + run: cargo test --doc --all-features - audit: + # Benchmark compilation check + bench: + runs-on: ubuntu-latest needs: check-skip - if: needs.check-skip.outputs.should_skip != 'true' - name: Security Audit + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_bench != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Check benchmarks + run: cargo bench --no-run --all-features + + # Security audit + audit: runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_audit != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' steps: - uses: actions/checkout@v4 - - uses: rustsec/audit-check@v1 + + - uses: dtolnay/rust-toolchain@stable + + - uses: rustsec/audit-check@v2 with: token: ${{ secrets.GITHUB_TOKEN }} + # Dependency policy check deny: - needs: check-skip - if: needs.check-skip.outputs.should_skip != 'true' - name: Cargo Deny runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && needs.check-skip.outputs.skip_deny != 'true' && needs.check-skip.outputs.has_rust_changes == 'true' steps: - uses: actions/checkout@v4 - - run: cargo install --locked cargo-deny - - run: cargo deny check + + - uses: dtolnay/rust-toolchain@1.81 + + - uses: Swatinem/rust-cache@v2 + + - name: Install cargo-deny + run: cargo install --locked cargo-deny + + - name: Run cargo-deny + run: cargo deny check diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 0000000..10a4688 --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,294 @@ +name: Security Scan + +on: + push: + branches: [master, main, develop] + pull_request: + branches: [master, main, develop] + schedule: + # Run security scans daily at 2 AM UTC + - cron: '0 2 * * *' + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + # Check if CI should be skipped + check-skip: + runs-on: ubuntu-latest + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - uses: actions/checkout@v4 + + - name: Check for .skip-ci file + id: skip_check + run: | + if [ -f .github/skips/.skip-ci ]; then + echo "should_skip=true" >> $GITHUB_OUTPUT + else + echo "should_skip=false" >> $GITHUB_OUTPUT + fi + + # Dependency scanning + dependency-review: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: moderate + + # Rust security audit + rust-security: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && hashFiles('Cargo.toml') != '' + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - name: Rust Security Audit + uses: rustsec/audit-check@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install cargo-deny + run: cargo install --locked cargo-deny + + - name: Run cargo-deny + run: cargo deny check + + # NPM/Node.js security audit + npm-security: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && hashFiles('package.json') != '' + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: NPM Audit + run: npm audit --audit-level=moderate + + - name: Check for vulnerabilities with snyk + uses: snyk/actions/node@master + continue-on-error: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + + # Python security audit + python-security: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && hashFiles('requirements.txt', 'Pipfile', 'pyproject.toml') != '' + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install safety + run: pip install safety + + - name: Run safety check + run: safety check --json + continue-on-error: true + + - name: Check for vulnerabilities with bandit + run: | + pip install bandit + bandit -r . -f json -o bandit-report.json + continue-on-error: true + + - name: Upload bandit report + if: always() + uses: actions/upload-artifact@v4 + with: + name: bandit-report + path: bandit-report.json + + # CodeQL analysis + codeql: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + strategy: + fail-fast: false + matrix: + language: [] # Auto-detect languages or specify: ['javascript', 'python', 'cpp', 'go', etc.] + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-extended,security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" + + # CRITICAL: Secret scanning (ALWAYS runs, cannot be skipped) + # These jobs protect against accidental secret commits + git-secrets: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install git-secrets + run: | + git clone https://github.com/awslabs/git-secrets.git + cd git-secrets + sudo make install + cd .. + rm -rf git-secrets + + - name: Configure git-secrets + run: | + git secrets --install + git secrets --register-aws + git secrets --add-provider -- cat .github/security/secrets-patterns.txt || true + + # Add common secret patterns + git secrets --add '[Pp]assword\s*[:=]\s*["\047]?[^\s"'\'']{8,}' + git secrets --add '[Aa]pi[_-]?[Kk]ey\s*[:=]\s*["\047]?[^\s"'\'']{16,}' + git secrets --add '[Tt]oken\s*[:=]\s*["\047]?[^\s"'\'']{16,}' + git secrets --add 'AIza[0-9A-Za-z\-_]{35}' + git secrets --add 'AKIA[0-9A-Z]{16}' + git secrets --add 'ghp_[0-9a-zA-Z]{36}' + git secrets --add 'glpat-[0-9a-zA-Z\-_]{20}' + git secrets --add 'sk-[0-9a-zA-Z]{48}' + git secrets --add 'xox[baprs]-[0-9a-zA-Z\-]{10,48}' + git secrets --add '-----BEGIN\s+(RSA|DSA|EC|OPENSSH|PGP)\s+PRIVATE\s+KEY' + + - name: Scan repository with git-secrets + run: git secrets --scan-history + + detect-secrets: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install detect-secrets + run: pip install detect-secrets + + - name: Create baseline (if not exists) + run: | + if [ ! -f .secrets.baseline ]; then + detect-secrets scan --all-files \ + --exclude-files '\.git/.*' \ + --exclude-files '.*\.lock' \ + --exclude-files 'package-lock\.json' \ + --exclude-files 'yarn\.lock' \ + --exclude-files 'Cargo\.lock' \ + > .secrets.baseline + fi + + - name: Scan for secrets + run: | + detect-secrets scan --baseline .secrets.baseline \ + --all-files \ + --exclude-files '\.git/.*' \ + --exclude-files '.*\.lock' \ + --exclude-files 'package-lock\.json' \ + --exclude-files 'yarn\.lock' \ + --exclude-files 'Cargo\.lock' + + - name: Audit baseline + run: detect-secrets audit .secrets.baseline --report --fail-on-unaudited --fail-on-live + + # Secret scanning with TruffleHog (ALWAYS runs) + trufflehog-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: TruffleHog Secret Scan + uses: trufflesecurity/trufflehog@main + with: + path: ./ + base: ${{ github.event.repository.default_branch }} + head: HEAD + extra_args: --debug --only-verified + + # Container security scanning + container-scan: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' && hashFiles('Dockerfile') != '' + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: docker build -t test-image:latest . + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: 'test-image:latest' + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH' + + - name: Upload Trivy results to GitHub Security + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'trivy-results.sarif' + + # License compliance check + license-check: + runs-on: ubuntu-latest + needs: check-skip + if: needs.check-skip.outputs.should_skip != 'true' + steps: + - uses: actions/checkout@v4 + + - name: Check Rust licenses + if: hashFiles('Cargo.toml') != '' + run: | + cargo install cargo-license + cargo license --json > licenses.json + cat licenses.json + + - name: Check NPM licenses + if: hashFiles('package.json') != '' + run: | + npm install -g license-checker + license-checker --json > npm-licenses.json + cat npm-licenses.json + + - name: Upload license reports + uses: actions/upload-artifact@v4 + with: + name: license-reports + path: | + licenses.json + npm-licenses.json diff --git a/.gitignore b/.gitignore index 591c6c8..0750bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,6 @@ Thumbs.db .server.pid *.pid *.sock + +# Security Baseline (generated by CI) +.secrets.baseline