{"id":3894,"date":"2025-06-27T18:08:15","date_gmt":"2025-06-27T16:08:15","guid":{"rendered":"https:\/\/nguenkam.com\/blog\/?p=3894"},"modified":"2025-07-09T16:12:40","modified_gmt":"2025-07-09T14:12:40","slug":"ggshield-guide-for-angular-spring-boot-development","status":"publish","type":"post","link":"https:\/\/nguenkam.com\/blog\/index.php\/2025\/06\/27\/ggshield-guide-for-angular-spring-boot-development\/","title":{"rendered":"ggshield Guide for Angular &#038; Spring Boot Development"},"content":{"rendered":"\n<p><strong>ggshield<\/strong> is GitGuardian&#8217;s CLI tool that helps developers detect and prevent secrets (API keys, passwords, tokens) from being committed to version control systems.<\/p>\n\n\n\n<h5><strong>Why is this crucial for Angular &amp; Spring Boot?<\/strong><\/h5>\n\n\n\n<ul><li><strong>Angular<\/strong>: Often contains API endpoints, authentication tokens, environment variables<\/li><li><strong>Spring Boot<\/strong>: Database credentials, JWT secrets, external service keys<\/li><li><strong>Security<\/strong>: Prevents accidental exposure of sensitive data<\/li><\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Big Picture: What Happens When we Use ggshield<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. You write code with secrets (accidentally)\n2. You try to commit ? ggshield scans your files\n3. ggshield finds secrets ? blocks the commit\n4. You fix the secrets ? commit succeeds\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Installation &amp; Setup<\/strong><\/h4>\n\n\n\n<h6><strong>Method 1: pip (Recommended)<\/strong><\/h6>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install ggshield<\/code><\/pre>\n\n\n\n<h6><strong>Method 2: Docker<\/strong><\/h6>\n\n\n\n<pre class=\"wp-block-code\"><code>docker pull gitguardian\/ggshield:latest<\/code><\/pre>\n\n\n\n<h4><strong>Authentication Setup<\/strong><\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code># Get your API key from GitGuardian dashboard\nggshield auth login\n\n# Or set environment variable\nexport GITGUARDIAN_API_KEY=\"your-api-key-here\"<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Angular Project Configuration<\/strong><\/h4>\n\n\n\n<h5><strong>1. Create .gitguardian.yaml<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml\nversion: 2\npaths-ignore:\n  - node_modules\/\n  - dist\/\n  - coverage\/\n  - .angular\/\n  - e2e\/results\/\n\npaths-scan:\n  - src\/\n  - angular.json\n  - package.json\n  - tsconfig.json\n\nsecret-scan-preference: secret\n\n# Angular-specific exclusions\nexclude-secrets:\n  - generic_api_key:\n      - \"ANGULAR_*\"  # Angular CLI variables\n      - \"NG_*\"       # Angular environment variables<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-red-color\">The Problem Without Configuration<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Without .gitguardian.yaml, ggshield would scan EVERYTHING:\nggshield secret scan .\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Scans node_modules\/ (thousands of files)\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Scans build outputs like dist\/, target\/\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Finds false positives in minified files\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Takes 10+ minutes to scan\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Reports fake secrets from dependencies\n<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-green-cyan-color\">How .gitguardian.yaml Solves This<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml - This is ggshield's INSTRUCTION MANUAL\nversion: 2\n\n# <img loading=\"lazy\" width=\"24\" height=\"25\" class=\"wp-image-3906\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/warning.png\" alt=\"\"> DON'T scan these folders (saves time, reduces noise)\npaths-ignore:\n  - node_modules\/     # Angular dependencies\n  - dist\/            # Angular build output\n  - target\/          # Spring Boot build output\n  - coverage\/        # Test coverage reports\n  - .angular\/        # Angular cache\n\n# <img loading=\"lazy\" width=\"20\" height=\"17\" class=\"wp-image-3905\" style=\"width: 20px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/correct.png\" alt=\"\"> ONLY scan these folders (focus on YOUR code)\npaths-scan:\n  - src\/             # Your actual source code\n  - angular.json     # Angular configuration\n  - application.yml  # Spring Boot configuration\n\n# <img loading=\"lazy\" width=\"22\" height=\"22\" class=\"wp-image-3907\" style=\"width: 22px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/ignore.png\" alt=\"\"> Ignore known safe \"secrets\" (reduces false positives)\nexclude-secrets:\n  - generic_api_key:\n      - \"ANGULAR_*\"    # Angular CLI generates these\n      - \"NG_*\"         # Angular environment prefixes\n      - \"demo-key-*\"   # Your demo\/test keys\n<\/code><\/pre>\n\n\n\n<p><strong>What Happens During Scan<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># When you run: ggshield secret scan .\n# ggshield reads .gitguardian.yaml and thinks:\n\n1. \"Should I scan node_modules\/? NO - it's in paths-ignore\"\n2. \"Should I scan src\/? YES - it's in paths-scan\"\n3. \"I found 'ANGULAR_CLI_TOKEN=abc123' - but it's in exclude-secrets, so I'll ignore it\"\n4. \"I found 'DATABASE_PASSWORD=realpassword123' - this is NOT excluded, so I'll report it!\"\n<\/code><\/pre>\n\n\n\n<h5><strong>2. Environment File Protection<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/environments\/environment.ts\nexport const environment = {\n  production: false,\n  apiUrl: process.env&#91;'API_URL'] || 'http:\/\/localhost:8080\/api',\n  \/\/ ? DON'T: apiKey: 'sk-1234567890abcdef'\n  \/\/ ? DO: Use environment variables\n  apiKey: process.env&#91;'API_KEY'] || ''\n};<\/code><\/pre>\n\n\n\n<h5><strong>3. Angular-specific .gitignore additions<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># Environment files\n.env\n.env.local\n.env.*.local\n\n# Angular specific\n\/dist\/\n\/tmp\/\n\/out-tsc\/\n\/bazel-out\/\n\n# IDE\n.vscode\/\n.idea\/\n\n# OS\n.DS_Store\nThumbs.db<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">How .gitignore Relates to GitGuardian<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitignore tells Git: \"Don't track these files\"\n# .gitguardian.yaml tells ggshield: \"Don't scan these files\"\n\n# They work TOGETHER for security:\n<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">Why We Need BOTH<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitignore - Prevents files from being committed\n.env                 # ? Contains real secrets\n.env.local          # ? Contains real secrets\n*.log               # ? Might contain sensitive data\nnode_modules\/       # ? Huge, not your code\ndist\/               # ? Build output, not source\n\n# .DS_Store          # ? macOS system files\n# Thumbs.db          # ? Windows system files\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml - Prevents scanning unnecessary files\npaths-ignore:\n  - node_modules\/    # ? Same as .gitignore (performance)\n  - dist\/           # ? Same as .gitignore (performance)\n  - \"*.log\"         # ? Same as .gitignore (avoid false positives)\n<\/code><\/pre>\n\n\n\n<p><strong>Real-World Example<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Without proper .gitignore:\necho \"DATABASE_PASSWORD=secret123\" &gt; .env\ngit add .env\ngit commit -m \"Add config\"\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Secret is now in Git history FOREVER!\n\n# With proper .gitignore:\necho \"DATABASE_PASSWORD=secret123\" &gt; .env\ngit add .env\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Git says: \"The following paths are ignored by one of your .gitignore files\"\n\n# With ggshield + .gitignore:\necho \"DATABASE_PASSWORD=secret123\" &gt; config.js  # Wrong file!\ngit add config.js\ngit commit -m \"Add config\"\n# <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> ggshield says: \"Secret detected! Commit blocked!\"\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Spring Boot Project Configuration<\/strong><\/h4>\n\n\n\n<p><strong>The Problem: Secrets in Code<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> BAD: Secrets directly in code\n@RestController\npublic class UserController {\n    private String jwtSecret = \"my-super-secret-jwt-key-123\";\n    private String dbPassword = \"prod-db-password-456\";\n    \n    \/\/ This will be detected by ggshield!\n}\n<\/code><\/pre>\n\n\n\n<p><strong>The Solution: Environment Variables<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># application.yml - Spring Boot configuration\nspring:\n  datasource:\n    url: jdbc:postgresql:\/\/localhost:5432\/mydb\n    username: ${DB_USERNAME:defaultuser}      # ? Environment variable\n    password: ${DB_PASSWORD:defaultpass}      # ? Environment variable\n  \nsecurity:\n  jwt:\n    secret: ${JWT_SECRET:default-dev-secret}  # ? Environment variable\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ <img loading=\"lazy\" width=\"20\" height=\"17\" class=\"wp-image-3905\" style=\"width: 20px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/correct.png\" alt=\"\"> GOOD: Using Spring's @Value annotation\n@RestController\npublic class UserController {\n    @Value(\"${security.jwt.secret}\")\n    private String jwtSecret;  \/\/ ? Gets value from application.yml\n    \n    @Value(\"${spring.datasource.password}\")\n    private String dbPassword; \/\/ ? Gets value from application.yml\n}\n<\/code><\/pre>\n\n\n\n<p><strong>How ggshield Protects This<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml - Configure for Spring Boot\nexclude-secrets:\n  - generic_password:\n      - \"spring.datasource.password\"  # ? Ignore this pattern in YAML files\n  - jwt:\n      - \"jwt.secret\"                  # ? Ignore this pattern in YAML files\n  - generic_api_key:\n      - \"SPRING_*\"                    # ? Ignore Spring environment variables\n<\/code><\/pre>\n\n\n\n<p><strong>Complete Workflow Example<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. Developer accidentally puts real secret in code\necho 'jwt.secret=prod-jwt-key-abc123' &gt;&gt; application.yml\n\n# 2. Developer tries to commit\ngit add application.yml\ngit commit -m \"Update JWT config\"\n\n# 3. ggshield (via pre-commit hook) scans the file\n# 4. ggshield reads .gitguardian.yaml\n# 5. ggshield finds 'prod-jwt-key-abc123' \n# 6. ggshield checks exclude-secrets - 'jwt.secret' is NOT excluded for REAL values\n# 7. ggshield BLOCKS the commit:\n\n<img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> SECRET DETECTED!\nFile: application.yml\nLine: 15\nSecret: JWT Token (confidence: 95%)\nValue: prod-jwt-key-abc123\n\n# 8. Developer fixes it:\necho 'jwt.secret=${JWT_SECRET:dev-secret}' &gt; application.yml\n\n# 9. Developer commits again - SUCCESS! ?\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h5><strong>1. Enhanced .gitguardian.yaml for Spring Boot<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml\nversion: 2\npaths-ignore:\n  - target\/\n  - .mvn\/\n  - node_modules\/\n  - logs\/\n  - *.log\n\npaths-scan:\n  - src\/\n  - pom.xml\n  - application*.yml\n  - application*.properties\n\n# Spring Boot specific exclusions\nexclude-secrets:\n  - generic_password:\n      - \"spring.datasource.password\"\n  - jwt:\n      - \"jwt.secret\"\n  - generic_api_key:\n      - \"SPRING_*\"<\/code><\/pre>\n\n\n\n<h5><strong>2. Secure Configuration Management<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># application.yml\nspring:\n  datasource:\n    url: jdbc:postgresql:\/\/localhost:5432\/mydb\n    username: ${DB_USERNAME:user}\n    password: ${DB_PASSWORD:password}  # Use environment variables\n\n  security:\n    jwt:\n      secret: ${JWT_SECRET:default-secret-for-dev}\n      expiration: 86400000\n\n# application-prod.yml\nspring:\n  datasource:\n    password: ${DB_PASSWORD}  # Must be set via environment\n  security:\n    jwt:\n      secret: ${JWT_SECRET}   # Must be set via environment<\/code><\/pre>\n\n\n\n<h5><strong>3. Maven\/Gradle Configuration<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!-- pom.xml - Exclude sensitive files from packaging --&gt;\n&lt;build&gt;\n    &lt;resources&gt;\n        &lt;resource&gt;\n            &lt;directory&gt;src\/main\/resources&lt;\/directory&gt;\n            &lt;excludes&gt;\n                &lt;exclude&gt;**\/*.key&lt;\/exclude&gt;\n                &lt;exclude&gt;**\/*.p12&lt;\/exclude&gt;\n                &lt;exclude&gt;**\/*.jks&lt;\/exclude&gt;\n            &lt;\/excludes&gt;\n        &lt;\/resource&gt;\n    &lt;\/resources&gt;\n&lt;\/build&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Pre-commit Hooks Setup<\/strong><\/h4>\n\n\n\n<h5><strong>1. Install pre-commit<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install pre-commit<\/code><\/pre>\n\n\n\n<h5><strong>2. Create .pre-commit-config.yaml<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .pre-commit-config.yaml\nrepos:\n  - repo: https:\/\/github.com\/gitguardian\/ggshield\n    rev: v1.25.0\n    hooks:\n      - id: ggshield\n        name: GitGuardian Shield\n        entry: ggshield secret scan pre-commit\n        language: python\n        stages: &#91;commit]\n        types: &#91;text]\n\n  # Additional hooks for Angular\/Spring Boot\n  - repo: https:\/\/github.com\/pre-commit\/pre-commit-hooks\n    rev: v4.4.0\n    hooks:\n      - id: trailing-whitespace\n      - id: end-of-file-fixer\n      - id: check-json\n      - id: check-yaml\n\n  # Angular specific\n  - repo: https:\/\/github.com\/pre-commit\/mirrors-eslint\n    rev: v8.44.0\n    hooks:\n      - id: eslint\n        files: \\.(js|ts)$\n        types: &#91;file]<\/code><\/pre>\n\n\n\n<h5><strong>3. Install and activate<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>pre-commit install\npre-commit run --all-files  # Test on all files<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">The Magic Behind the Scenes<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># When you install pre-commit hooks:\npre-commit install\n\n# This creates: .git\/hooks\/pre-commit\n# Every time you run 'git commit', this script runs FIRST\n<\/code><\/pre>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">What Happens During Commit<\/span><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># You type: git commit -m \"Add new feature\"\n\n# Git automatically runs:\n1. .git\/hooks\/pre-commit\n2. pre-commit runs all hooks from .pre-commit-config.yaml \n3. ggshield secret scan pre-commit \n4. ggshield reads .gitguardian.yaml for configuration \n5. ggshield scans only the files you're trying to commit \n6. If secrets found: BLOCK commit <img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\">\n   If no secrets: Allow commit <img loading=\"lazy\" width=\"20\" height=\"17\" class=\"wp-image-3905\" style=\"width: 20px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/correct.png\" alt=\"\">\n<\/code><\/pre>\n\n\n\n<p><strong>Visual Example<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Normal commit without ggshield:\ngit add file-with-secret.js\ngit commit -m \"Add feature\"\n# <img loading=\"lazy\" width=\"20\" height=\"17\" class=\"wp-image-3905\" style=\"width: 20px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/correct.png\" alt=\"\"> Commit succeeds (BAD!)\n\n# Commit with ggshield pre-commit hook:\ngit add file-with-secret.js\ngit commit -m \"Add feature\"\n\n# ggshield runs automatically:\nScanning staged files...\n<img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Secret detected in file-with-secret.js\n<img loading=\"lazy\" width=\"24\" height=\"24\" class=\"wp-image-3904\" style=\"width: 24px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/false.png\" alt=\"\"> Commit blocked!\n\n# Fix the secret:\n# Remove secret from file-with-secret.js\ngit add file-with-secret.js\ngit commit -m \"Add feature\"\n# <img loading=\"lazy\" width=\"20\" height=\"17\" class=\"wp-image-3905\" style=\"width: 20px;\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/correct.png\" alt=\"\"> Commit succeeds (GOOD!)\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>CI\/CD Integration<\/strong><\/h4>\n\n\n\n<h5><strong>GitHub Actions Example<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .github\/workflows\/security-scan.yml\nname: Security Scan\n\non:\n  push:\n    branches: &#91; main, develop ]\n  pull_request:\n    branches: &#91; main ]\n\njobs:\n  security-scan:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions\/checkout@v3\n      with:\n        fetch-depth: 0  # Full history for better scanning\n\n    - name: Setup Python\n      uses: actions\/setup-python@v4\n      with:\n        python-version: '3.9'\n\n    - name: Install ggshield\n      run: pip install ggshield\n\n    - name: Scan for secrets\n      env:\n        GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}\n      run: ggshield secret scan ci\n\n    # Angular specific steps\n    - name: Setup Node.js\n      uses: actions\/setup-node@v3\n      with:\n        node-version: '18'\n\n    - name: Install Angular dependencies\n      run: npm ci\n\n    - name: Angular Security Audit\n      run: npm audit --audit-level moderate\n\n    # Spring Boot specific steps\n    - name: Setup Java\n      uses: actions\/setup-java@v3\n      with:\n        java-version: '17'\n        distribution: 'temurin'\n\n    - name: Maven Security Check\n      run: mvn org.owasp:dependency-check-maven:check<\/code><\/pre>\n\n\n\n<h5><strong>GitLab CI Example<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitlab-ci.yml\nstages:\n  - security\n  - build\n  - test\n\nsecurity-scan:\n  stage: security\n  image: python:3.9\n  before_script:\n    - pip install ggshield\n  script:\n    - ggshield secret scan ci\n  variables:\n    GITGUARDIAN_API_KEY: $GITGUARDIAN_API_KEY\n  only:\n    - merge_requests\n    - main\n    - develop<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong><span class=\"has-inline-color has-vivid-green-cyan-color\">Project Structure<\/span><\/strong><\/h4>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"626\" height=\"314\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-1.png\" alt=\"\" class=\"wp-image-3895\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-1.png 626w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-1-300x150.png 300w\" sizes=\"(max-width: 626px) 100vw, 626px\" \/><\/figure>\n\n\n\n<ul><li><strong>Step 1: .gitignore protects sensitive files<\/strong><\/li><li><strong>&nbsp;.gitguardian.yaml configures scanning<\/strong><\/li><li><strong>.pre-commit-config.yaml sets up automatic scanning<\/strong> <\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"446\" height=\"180\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-2.png\" alt=\"\" class=\"wp-image-3896\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-2.png 446w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-2-300x121.png 300w\" sizes=\"(max-width: 446px) 100vw, 446px\" \/><\/figure>\n\n\n\n<ul><li><strong>Step 4: Safe configuration files<\/strong><\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"896\" height=\"297\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-3.png\" alt=\"\" class=\"wp-image-3897\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-3.png 896w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-3-300x99.png 300w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-3-768x255.png 768w\" sizes=\"(max-width: 896px) 100vw, 896px\" \/><\/figure>\n\n\n\n<ul><li><strong>Step 5: Environment template<\/strong><\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"492\" height=\"135\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-4.png\" alt=\"\" class=\"wp-image-3898\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-4.png 492w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-4-300x82.png 300w\" sizes=\"(max-width: 492px) 100vw, 492px\" \/><\/figure>\n\n\n\n<ul><li><strong>Step 6: Real environment file<\/strong><\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"550\" height=\"148\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-5.png\" alt=\"\" class=\"wp-image-3899\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-5.png 550w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-5-300x81.png 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"942\" height=\"360\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-6.png\" alt=\"\" class=\"wp-image-3900\" srcset=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-6.png 942w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-6-300x115.png 300w, https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2025\/06\/image-6-768x294.png 768w\" sizes=\"(max-width: 942px) 100vw, 942px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Best Practices<\/strong><\/h4>\n\n\n\n<h5><strong>1. Development Workflow<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># Daily workflow\ngit add .\ngit commit -m \"feat: add user authentication\"\n# ggshield automatically scans via pre-commit hook\n\n# Manual scan before push\nggshield secret scan repo .\n\n# Scan specific files\nggshield secret scan path src\/environments\/<\/code><\/pre>\n\n\n\n<h5><strong>2. Environment Management<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .env.example (commit this)\nAPI_URL=http:\/\/localhost:8080\/api\nDB_HOST=localhost\nDB_PORT=5432\nDB_NAME=myapp\nJWT_SECRET=your-jwt-secret-here\nGITGUARDIAN_API_KEY=your-api-key-here\n\n# .env (never commit this)\nAPI_URL=https:\/\/api.production.com\nDB_HOST=prod-db.company.com\nDB_PORT=5432\nDB_NAME=prod_myapp\nJWT_SECRET=actual-production-secret\nGITGUARDIAN_API_KEY=actual-api-key<\/code><\/pre>\n\n\n\n<h5><strong>3. Team Configuration<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml - Team settings\nversion: 2\nverbose: true\nshow-secrets: false  # Don't show actual secrets in output\n\n# Custom rules for your team\nexclude-secrets:\n  - generic_api_key:\n      - \"DEMO_*\"      # Demo\/test keys\n      - \"MOCK_*\"      # Mock data\n      - \"TEST_*\"      # Test environment\n\n# Incident management\non-secret-found:\n  - webhook: \"https:\/\/your-team-webhook.com\/security-alert\"\n  - email: \"security-team@company.com\"<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Troubleshooting<\/strong><\/h4>\n\n\n\n<h4><strong>Common Issues &amp; Solutions<\/strong><\/h4>\n\n\n\n<h5><strong>1. False Positives<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># Add to .gitguardian.yaml\nexclude-secrets:\n  - generic_api_key:\n      - \"example-key-123\"  # Known safe example\n      - \"demo-token-*\"     # Demo patterns<\/code><\/pre>\n\n\n\n<h5><strong>2. Large Repository Scanning<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># Scan only recent commits\nggshield secret scan ci --max-commits-for-hook 10\n\n# Scan specific branch\nggshield secret scan repo --branch feature\/new-auth<\/code><\/pre>\n\n\n\n<h5><strong>3. Performance Issues<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># .gitguardian.yaml - Optimize performance\npaths-ignore:\n  - \"**\/*.min.js\"      # Minified files\n  - \"**\/*.bundle.js\"   # Bundled files\n  - \"**\/node_modules\/\" # Dependencies\n  - \"**\/target\/\"       # Build artifacts\n  - \"**\/*.log\"         # Log files<\/code><\/pre>\n\n\n\n<h5><strong>4. Integration Issues<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># Debug mode\nggshield --debug secret scan path .\n\n# Check configuration\nggshield config list\n\n# Test API connection\nggshield api-status<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Monitoring &amp; Reporting<\/strong><\/h4>\n\n\n\n<h5><strong>1. Dashboard Integration<\/strong><\/h5>\n\n\n\n<ul><li>Access GitGuardian dashboard for incident tracking<\/li><li>Set up team notifications<\/li><li>Monitor scan statistics<\/li><\/ul>\n\n\n\n<h5><strong>2. Custom Reporting<\/strong><\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code># Generate scan report\nggshield secret scan repo . --json &gt; security-report.json\n\n# Integrate with monitoring tools\nggshield secret scan ci --output sarif &gt; results.sarif<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h4><strong>Summary<\/strong><\/h4>\n\n\n\n<p>By implementing ggshield in our Angular and Spring Boot development workflow, we:<\/p>\n\n\n\n<p> <strong>Prevent<\/strong> secret leaks before they reach version control<br> <strong>Automate<\/strong> security scanning in CI\/CD pipelines<br> <strong>Maintain<\/strong> clean commit history without sensitive data<br> <strong>Comply<\/strong> with security best practices<br> <strong>Protect<\/strong> our applications and infrastructure<\/p>\n\n\n\n<p><strong>Remember<\/strong>: Security is not a one-time setup but an ongoing process. Regularly update our configurations and stay informed about new security practices! ?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ggshield is GitGuardian&#8217;s CLI tool that helps developers detect and prevent secrets (API keys, passwords, tokens) from being committed to version control systems. Why is this crucial for Angular &amp; Spring Boot? Angular: Often contains API endpoints, authentication tokens, environment variables Spring Boot: Database credentials, JWT secrets, external service keys Security: Prevents accidental exposure of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3901,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1038,1037,1036],"tags":[952,1040,1045,1039,707,1047,1043,1044,1041,1042,1048,1046],"_links":{"self":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3894"}],"collection":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=3894"}],"version-history":[{"count":3,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3894\/revisions"}],"predecessor-version":[{"id":3938,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/3894\/revisions\/3938"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media\/3901"}],"wp:attachment":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=3894"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=3894"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=3894"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}