ggshield is GitGuardian’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 & 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 sensitive data

Big Picture: What Happens When we Use ggshield

1. You write code with secrets (accidentally)
2. You try to commit ? ggshield scans your files
3. ggshield finds secrets ? blocks the commit
4. You fix the secrets ? commit succeeds


Installation & Setup

Method 1: pip (Recommended)
pip install ggshield
Method 2: Docker
docker pull gitguardian/ggshield:latest

Authentication Setup

# Get your API key from GitGuardian dashboard
ggshield auth login

# Or set environment variable
export GITGUARDIAN_API_KEY="your-api-key-here"

Angular Project Configuration

1. Create .gitguardian.yaml
# .gitguardian.yaml
version: 2
paths-ignore:
  - node_modules/
  - dist/
  - coverage/
  - .angular/
  - e2e/results/

paths-scan:
  - src/
  - angular.json
  - package.json
  - tsconfig.json

secret-scan-preference: secret

# Angular-specific exclusions
exclude-secrets:
  - generic_api_key:
      - "ANGULAR_*"  # Angular CLI variables
      - "NG_*"       # Angular environment variables

The Problem Without Configuration

# Without .gitguardian.yaml, ggshield would scan EVERYTHING:
ggshield secret scan .
#  Scans node_modules/ (thousands of files)
#  Scans build outputs like dist/, target/
#  Finds false positives in minified files
#  Takes 10+ minutes to scan
#  Reports fake secrets from dependencies

How .gitguardian.yaml Solves This

# .gitguardian.yaml - This is ggshield's INSTRUCTION MANUAL
version: 2

#  DON'T scan these folders (saves time, reduces noise)
paths-ignore:
  - node_modules/     # Angular dependencies
  - dist/            # Angular build output
  - target/          # Spring Boot build output
  - coverage/        # Test coverage reports
  - .angular/        # Angular cache

#  ONLY scan these folders (focus on YOUR code)
paths-scan:
  - src/             # Your actual source code
  - angular.json     # Angular configuration
  - application.yml  # Spring Boot configuration

#  Ignore known safe "secrets" (reduces false positives)
exclude-secrets:
  - generic_api_key:
      - "ANGULAR_*"    # Angular CLI generates these
      - "NG_*"         # Angular environment prefixes
      - "demo-key-*"   # Your demo/test keys

What Happens During Scan

# When you run: ggshield secret scan .
# ggshield reads .gitguardian.yaml and thinks:

1. "Should I scan node_modules/? NO - it's in paths-ignore"
2. "Should I scan src/? YES - it's in paths-scan"
3. "I found 'ANGULAR_CLI_TOKEN=abc123' - but it's in exclude-secrets, so I'll ignore it"
4. "I found 'DATABASE_PASSWORD=realpassword123' - this is NOT excluded, so I'll report it!"
2. Environment File Protection
// src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: process.env['API_URL'] || 'http://localhost:8080/api',
  // ? DON'T: apiKey: 'sk-1234567890abcdef'
  // ? DO: Use environment variables
  apiKey: process.env['API_KEY'] || ''
};
3. Angular-specific .gitignore additions
# Environment files
.env
.env.local
.env.*.local

# Angular specific
/dist/
/tmp/
/out-tsc/
/bazel-out/

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

How .gitignore Relates to GitGuardian

# .gitignore tells Git: "Don't track these files"
# .gitguardian.yaml tells ggshield: "Don't scan these files"

# They work TOGETHER for security:

Why We Need BOTH

# .gitignore - Prevents files from being committed
.env                 # ? Contains real secrets
.env.local          # ? Contains real secrets
*.log               # ? Might contain sensitive data
node_modules/       # ? Huge, not your code
dist/               # ? Build output, not source

# .DS_Store          # ? macOS system files
# Thumbs.db          # ? Windows system files
# .gitguardian.yaml - Prevents scanning unnecessary files
paths-ignore:
  - node_modules/    # ? Same as .gitignore (performance)
  - dist/           # ? Same as .gitignore (performance)
  - "*.log"         # ? Same as .gitignore (avoid false positives)

Real-World Example

# Without proper .gitignore:
echo "DATABASE_PASSWORD=secret123" > .env
git add .env
git commit -m "Add config"
#  Secret is now in Git history FOREVER!

# With proper .gitignore:
echo "DATABASE_PASSWORD=secret123" > .env
git add .env
#  Git says: "The following paths are ignored by one of your .gitignore files"

# With ggshield + .gitignore:
echo "DATABASE_PASSWORD=secret123" > config.js  # Wrong file!
git add config.js
git commit -m "Add config"
#  ggshield says: "Secret detected! Commit blocked!"

Spring Boot Project Configuration

The Problem: Secrets in Code

//  BAD: Secrets directly in code
@RestController
public class UserController {
    private String jwtSecret = "my-super-secret-jwt-key-123";
    private String dbPassword = "prod-db-password-456";
    
    // This will be detected by ggshield!
}

The Solution: Environment Variables

# application.yml - Spring Boot configuration
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: ${DB_USERNAME:defaultuser}      # ? Environment variable
    password: ${DB_PASSWORD:defaultpass}      # ? Environment variable
  
security:
  jwt:
    secret: ${JWT_SECRET:default-dev-secret}  # ? Environment variable
//  GOOD: Using Spring's @Value annotation
@RestController
public class UserController {
    @Value("${security.jwt.secret}")
    private String jwtSecret;  // ? Gets value from application.yml
    
    @Value("${spring.datasource.password}")
    private String dbPassword; // ? Gets value from application.yml
}

How ggshield Protects This

# .gitguardian.yaml - Configure for Spring Boot
exclude-secrets:
  - generic_password:
      - "spring.datasource.password"  # ? Ignore this pattern in YAML files
  - jwt:
      - "jwt.secret"                  # ? Ignore this pattern in YAML files
  - generic_api_key:
      - "SPRING_*"                    # ? Ignore Spring environment variables

Complete Workflow Example

# 1. Developer accidentally puts real secret in code
echo 'jwt.secret=prod-jwt-key-abc123' >> application.yml

# 2. Developer tries to commit
git add application.yml
git commit -m "Update JWT config"

# 3. ggshield (via pre-commit hook) scans the file
# 4. ggshield reads .gitguardian.yaml
# 5. ggshield finds 'prod-jwt-key-abc123' 
# 6. ggshield checks exclude-secrets - 'jwt.secret' is NOT excluded for REAL values
# 7. ggshield BLOCKS the commit:

 SECRET DETECTED!
File: application.yml
Line: 15
Secret: JWT Token (confidence: 95%)
Value: prod-jwt-key-abc123

# 8. Developer fixes it:
echo 'jwt.secret=${JWT_SECRET:dev-secret}' > application.yml

# 9. Developer commits again - SUCCESS! ?

1. Enhanced .gitguardian.yaml for Spring Boot
# .gitguardian.yaml
version: 2
paths-ignore:
  - target/
  - .mvn/
  - node_modules/
  - logs/
  - *.log

paths-scan:
  - src/
  - pom.xml
  - application*.yml
  - application*.properties

# Spring Boot specific exclusions
exclude-secrets:
  - generic_password:
      - "spring.datasource.password"
  - jwt:
      - "jwt.secret"
  - generic_api_key:
      - "SPRING_*"
2. Secure Configuration Management
# application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: ${DB_USERNAME:user}
    password: ${DB_PASSWORD:password}  # Use environment variables

  security:
    jwt:
      secret: ${JWT_SECRET:default-secret-for-dev}
      expiration: 86400000

# application-prod.yml
spring:
  datasource:
    password: ${DB_PASSWORD}  # Must be set via environment
  security:
    jwt:
      secret: ${JWT_SECRET}   # Must be set via environment
3. Maven/Gradle Configuration
<!-- pom.xml - Exclude sensitive files from packaging -->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <excludes>
                <exclude>**/*.key</exclude>
                <exclude>**/*.p12</exclude>
                <exclude>**/*.jks</exclude>
            </excludes>
        </resource>
    </resources>
</build>

Pre-commit Hooks Setup

1. Install pre-commit
pip install pre-commit
2. Create .pre-commit-config.yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitguardian/ggshield
    rev: v1.25.0
    hooks:
      - id: ggshield
        name: GitGuardian Shield
        entry: ggshield secret scan pre-commit
        language: python
        stages: [commit]
        types: [text]

  # Additional hooks for Angular/Spring Boot
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-json
      - id: check-yaml

  # Angular specific
  - repo: https://github.com/pre-commit/mirrors-eslint
    rev: v8.44.0
    hooks:
      - id: eslint
        files: \.(js|ts)$
        types: [file]
3. Install and activate
pre-commit install
pre-commit run --all-files  # Test on all files

The Magic Behind the Scenes

# When you install pre-commit hooks:
pre-commit install

# This creates: .git/hooks/pre-commit
# Every time you run 'git commit', this script runs FIRST

What Happens During Commit

# You type: git commit -m "Add new feature"

# Git automatically runs:
1. .git/hooks/pre-commit
2. pre-commit runs all hooks from .pre-commit-config.yaml 
3. ggshield secret scan pre-commit 
4. ggshield reads .gitguardian.yaml for configuration 
5. ggshield scans only the files you're trying to commit 
6. If secrets found: BLOCK commit 
   If no secrets: Allow commit 

Visual Example

# Normal commit without ggshield:
git add file-with-secret.js
git commit -m "Add feature"
#  Commit succeeds (BAD!)

# Commit with ggshield pre-commit hook:
git add file-with-secret.js
git commit -m "Add feature"

# ggshield runs automatically:
Scanning staged files...
 Secret detected in file-with-secret.js
 Commit blocked!

# Fix the secret:
# Remove secret from file-with-secret.js
git add file-with-secret.js
git commit -m "Add feature"
#  Commit succeeds (GOOD!)

CI/CD Integration

GitHub Actions Example
# .github/workflows/security-scan.yml
name: Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  security-scan:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
      with:
        fetch-depth: 0  # Full history for better scanning

    - name: Setup Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.9'

    - name: Install ggshield
      run: pip install ggshield

    - name: Scan for secrets
      env:
        GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
      run: ggshield secret scan ci

    # Angular specific steps
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'

    - name: Install Angular dependencies
      run: npm ci

    - name: Angular Security Audit
      run: npm audit --audit-level moderate

    # Spring Boot specific steps
    - name: Setup Java
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Maven Security Check
      run: mvn org.owasp:dependency-check-maven:check
GitLab CI Example
# .gitlab-ci.yml
stages:
  - security
  - build
  - test

security-scan:
  stage: security
  image: python:3.9
  before_script:
    - pip install ggshield
  script:
    - ggshield secret scan ci
  variables:
    GITGUARDIAN_API_KEY: $GITGUARDIAN_API_KEY
  only:
    - merge_requests
    - main
    - develop

Project Structure

  • Step 1: .gitignore protects sensitive files
  •  .gitguardian.yaml configures scanning
  • .pre-commit-config.yaml sets up automatic scanning
  • Step 4: Safe configuration files
  • Step 5: Environment template
  • Step 6: Real environment file

Best Practices

1. Development Workflow
# Daily workflow
git add .
git commit -m "feat: add user authentication"
# ggshield automatically scans via pre-commit hook

# Manual scan before push
ggshield secret scan repo .

# Scan specific files
ggshield secret scan path src/environments/
2. Environment Management
# .env.example (commit this)
API_URL=http://localhost:8080/api
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
JWT_SECRET=your-jwt-secret-here
GITGUARDIAN_API_KEY=your-api-key-here

# .env (never commit this)
API_URL=https://api.production.com
DB_HOST=prod-db.company.com
DB_PORT=5432
DB_NAME=prod_myapp
JWT_SECRET=actual-production-secret
GITGUARDIAN_API_KEY=actual-api-key
3. Team Configuration
# .gitguardian.yaml - Team settings
version: 2
verbose: true
show-secrets: false  # Don't show actual secrets in output

# Custom rules for your team
exclude-secrets:
  - generic_api_key:
      - "DEMO_*"      # Demo/test keys
      - "MOCK_*"      # Mock data
      - "TEST_*"      # Test environment

# Incident management
on-secret-found:
  - webhook: "https://your-team-webhook.com/security-alert"
  - email: "security-team@company.com"

Troubleshooting

Common Issues & Solutions

1. False Positives
# Add to .gitguardian.yaml
exclude-secrets:
  - generic_api_key:
      - "example-key-123"  # Known safe example
      - "demo-token-*"     # Demo patterns
2. Large Repository Scanning
# Scan only recent commits
ggshield secret scan ci --max-commits-for-hook 10

# Scan specific branch
ggshield secret scan repo --branch feature/new-auth
3. Performance Issues
# .gitguardian.yaml - Optimize performance
paths-ignore:
  - "**/*.min.js"      # Minified files
  - "**/*.bundle.js"   # Bundled files
  - "**/node_modules/" # Dependencies
  - "**/target/"       # Build artifacts
  - "**/*.log"         # Log files
4. Integration Issues
# Debug mode
ggshield --debug secret scan path .

# Check configuration
ggshield config list

# Test API connection
ggshield api-status

Monitoring & Reporting

1. Dashboard Integration
  • Access GitGuardian dashboard for incident tracking
  • Set up team notifications
  • Monitor scan statistics
2. Custom Reporting
# Generate scan report
ggshield secret scan repo . --json > security-report.json

# Integrate with monitoring tools
ggshield secret scan ci --output sarif > results.sarif

Summary

By implementing ggshield in our Angular and Spring Boot development workflow, we:

Prevent secret leaks before they reach version control
Automate security scanning in CI/CD pipelines
Maintain clean commit history without sensitive data
Comply with security best practices
Protect our applications and infrastructure

Remember: Security is not a one-time setup but an ongoing process. Regularly update our configurations and stay informed about new security practices! ?

By Shabazz

Software Engineer, MCSD, Web developer & Angular specialist

Leave a Reply

Your email address will not be published. Required fields are marked *