Designing and being involved in the development of modern web applications, I've learned that security vulnerabilities can expose systems to serious risks, from data breaches to service disruptions. Whether I'm building with React on the frontend or .NET APIs on the backend, I've made it a habit to regularly scan for and address vulnerabilities as a critical part of my development workflow.

In this guide, I'll share the tools and processes I use daily for identifying and resolving security vulnerabilities in both my React and .NET applications. These are the actual practices I follow in my own projects.

Table of Contents

  1. How I Check Vulnerabilities in My React Applications
  2. How I Check Vulnerabilities in My .NET Solutions
  3. My Best Practices for Vulnerability Management
  4. How I Automate Security Scanning in CI/CD
  5. My Conclusions

Checking Vulnerabilities in React Applications

1. Using npm audit - My Go-To Tool

The most straightforward way I check for vulnerabilities in my React projects is using npm's built-in audit command. I run this almost daily on my frontend projects.

How I Run a Basic Vulnerability Scan

# I navigate to my React project directory
cd frontend

# Then I run npm audit
npm audit

When I run this command, it:

  • Scans my package.json and package-lock.json
  • Compares my dependencies against the npm vulnerability database
  • Shows me a report with severity levels (low, moderate, high, critical)

Understanding What I See in the Output

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ High          β”‚ Prototype Pollution in json-schema                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Package       β”‚ json-schema                                                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Patched in    β”‚ >=0.4.0                                                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Dependency of β”‚ react-scripts                                                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Path          β”‚ react-scripts > webpack > json-schema                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How I Automatically Fix Issues

# I let npm automatically fix vulnerabilities where possible
npm audit fix

# Sometimes I use force for major version updates (but I'm always careful!)
npm audit fix --force

⚠️ Warning from my experience: I've learned that npm audit fix --force can introduce breaking changes. I always test thoroughly after running this command - trust me, I've been burned by this before!

2. Using Yarn - My Alternative Approach

When I'm working on projects that use Yarn instead of npm, here's what I do:

# Check for vulnerabilities
yarn audit

# Interactive fix
yarn audit fix

In my experience, Yarn's audit output is sometimes clearer than npm's, especially when dealing with complex dependency trees.

3. Advanced Scanning with Snyk - My Favorite Tool

Snyk is what I use when I need a more comprehensive security analysis. I've integrated it into my projects and it's been a game-changer.

How I Installed Snyk

npm install -g snyk

How I Authenticate and Scan My Projects

# First, I authenticate with Snyk
snyk auth

# Then I test my project
snyk test

# I also set up continuous monitoring for my production apps
snyk monitor

Why I Love Snyk

  • Detailed remediation advice: It gives me step-by-step guidance on fixing issues
  • License compliance: It helps me check for licensing issues
  • Docker scanning: I can scan my container images
  • IDE integration: I get real-time vulnerability detection right in VS Code

4. Using OWASP Dependency-Check - For Comprehensive Analysis

When I need really comprehensive dependency analysis, I use this:

# Install dependency-check
npm install -g dependency-check

# Run scan
dependency-check --project "My React App" --scan ./frontend

From my experience, this tool is particularly useful when I'm preparing for security audits or need to generate detailed reports for compliance purposes.

5. How I Resolve React Vulnerabilities

My Strategy 1: Update Dependencies

This is usually my first approach:

# I update a specific package that has issues
npm update package-name

# Or I update all packages to their latest compatible versions
npm update

# I always check what's outdated first
npm outdated

My Strategy 2: Replace Vulnerable Packages

When a package has unresolved vulnerabilities, here's what I do:

  1. I search for alternatives: npm search alternative-package
  2. I check GitHub to see if it's actively maintained
  3. If I really need that specific package, I consider forking and patching it myself

In my experience, it's often better to find a well-maintained alternative than to stick with an abandoned package, even if it means refactoring some code.

My Strategy 3: Override Dependencies (My Last Resort)

When nothing else works, I use npm overrides in my package.json:

{
  "overrides": {
    "vulnerable-package": "^safe-version"
  }
}

Checking Vulnerabilities in .NET Solutions

1. Using dotnet list package - My Primary .NET Tool

The .NET CLI includes built-in vulnerability scanning, and I use it extensively on my backend projects.

How I Check for Vulnerable Packages

# I navigate to my solution directory
cd backend

# Then I list packages with vulnerabilities
dotnet list package --vulnerable

# I also include transitive dependencies to catch everything
dotnet list package --vulnerable --include-transitive

What I See in My Output

Project `MyApi` has the following vulnerable packages
   [net8.0]:
   Top-level Package      Requested   Resolved   Severity   Advisory URL
   > Newtonsoft.Json      12.0.1      12.0.1     High       https://github.com/advisories/GHSA-5crp-9r3c-p9vr

2. How I Check for Outdated Packages

I regularly run these commands to stay on top of updates:

# I list outdated packages
dotnet list package --outdated

# I check for the highest patch, minor, and major versions
dotnet list package --outdated --highest-patch
dotnet list package --outdated --highest-minor

From what I've learned, staying on top of updates is much easier than dealing with a massive backlog of outdated packages later.

3. Using NuGet Package Manager in Visual Studio

When I'm working in Visual Studio, I also use the GUI:

  1. I right-click on the solution in Solution Explorer
  2. I select "Manage NuGet Packages for Solution..."
  3. I click the "Updates" tab
  4. I look for packages with a security warning icon πŸ”’

4. Advanced Scanning with OWASP Dependency-Check for My .NET Projects

# Install dependency-check-cli
# Download from https://github.com/dependency-check/DependencyCheck

# Run scan
dependency-check.bat --project "My API" --scan ".\backend" --format HTML --out ".\reports"

In my experience, the HTML reports from this tool are excellent for sharing with non-technical stakeholders - they're detailed yet easy to understand.

5. How I Use Snyk for .NET

I also use Snyk for my .NET projects:

# I scan my .NET solution
snyk test --file=MySolution.sln

# And monitor it continuously
snyk monitor --file=MySolution.sln

6. How I Stay Updated with Microsoft's Security Advisories

I regularly check the official .NET Security GitHub repo: https://github.com/dotnet/announcements/issues

I've also subscribed to security announcements so I get notified immediately when something important comes up.

7. How I Resolve .NET Vulnerabilities

My Strategy 1: Update NuGet Packages

This is usually where I start:

# I update a specific package across all my projects
dotnet add package PackageName --version latest

# Or I update it in a specific project
cd .\backend\src\MyApi
dotnet add package PackageName --version latest

My Strategy 2: Update Transitive Dependencies

Sometimes vulnerabilities are in transitive dependencies. Here's what I do to force an update:

<!-- In my .csproj file, I add this -->
<ItemGroup>
  <PackageReference Include="VulnerablePackage" Version="SafeVersion" />
</ItemGroup>

My Strategy 3: Use Central Package Management

I've started using Central Package Management in my projects. Here's how I set it up:

I create a Directory.Packages.props file in my solution root:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
    <PackageVersion Include="Serilog" Version="3.1.1" />
  </ItemGroup>
</Project>

Then in my .csproj files, I don't specify versions anymore:

<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" />
  <PackageReference Include="Serilog" />
</ItemGroup>

Based on my experience, this approach has been a game-changer for managing package versions across multiple projects in my solution - one place to update, and all projects benefit.

My Strategy 4: Use .NET Security Policies

I configure vulnerability severity thresholds in my projects to fail builds when issues are found:

<PropertyGroup>
  <NuGetAudit>true</NuGetAudit>
  <NuGetAuditMode>direct</NuGetAuditMode>
  <NuGetAuditLevel>low</NuGetAuditLevel>
</PropertyGroup>

This will cause my builds to fail if vulnerabilities are detected above the specified level. It's saved me several times!

Best Practices for Vulnerability Management

1. My Regular Scanning Schedule

I created a weekly scan script that I run every Monday morning. Here's what it looks like:

# Create a weekly scan script (PowerShell)
# scan-vulnerabilities.ps1

Write-Host "Scanning Frontend..." -ForegroundColor Cyan
cd frontend
npm audit
npm outdated

Write-Host "`nScanning Backend..." -ForegroundColor Cyan
cd ../backend
dotnet list package --vulnerable --include-transitive
dotnet list package --outdated

In my experience, having a consistent schedule makes security scanning a habit rather than an afterthought. I literally have this blocked on my calendar every Monday at 9 AM.

2. How I Handle Dependency Pinning vs. Range

I've learned to be strategic about version specifications:

For My Production Code:

{
  "dependencies": {
    "react": "19.0.0",  // Exact version
    "axios": "~1.6.0"    // Patch updates only
  }
}

For My Development Projects:

{
  "dependencies": {
    "react": "^19.0.0"  // Minor updates allowed
  }
}

From my experience, using exact versions in production has saved me from several "it works on my machine" scenarios. I learned this the hard way after a minor version update broke our production build.

3. My Security-First Dependency Selection Process

Before I add any new package to my projects, I always check:

  • npm/NuGet download statistics: I prefer popular packages because they're usually better maintained
  • GitHub activity: I look for recent commits - they indicate active maintenance
  • Security history: I check if there have been past vulnerabilities and how quickly they were fixed
  • License compatibility: I ensure it won't cause legal issues for my project

4. How I Implement Lockfiles

For My Frontend:

  • I always commit package-lock.json or yarn.lock to my repo
  • I use npm ci in my CI/CD instead of npm install for deterministic builds

For My Backend:

  • I commit packages.lock.json when using Central Package Management
  • I use deterministic restore: dotnet restore --locked-mode

In my experience, lockfiles are non-negotiable. I once spent two hours debugging an issue that turned out to be caused by inconsistent dependency versions across team members' machines. Never again!

5. My Security Policies with Dependabot

I've set up Dependabot in my GitHub repo. Here's my .github/dependabot.yml:

version: 2
updates:
  # Frontend dependencies
  - package-ecosystem: "npm"
    directory: "/frontend"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10
    
  # Backend dependencies
  - package-ecosystem: "nuget"
    directory: "/backend"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

Automated CI/CD Integration

My GitHub Actions Setup

I've set up automated security scanning in my GitHub Actions. Here's the workflow I use for my projects:

Create .github/workflows/security-scan.yml:

name: Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]
  schedule:
    # Run every Monday at 9 AM
    - cron: '0 9 * * 1'

jobs:
  frontend-security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - name: Install dependencies
        run: |
          cd frontend
          npm ci
          
      - name: Run npm audit
        run: |
          cd frontend
          npm audit --audit-level=moderate
          
      - name: Run Snyk
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

  backend-security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'
          
      - name: Restore dependencies
        run: |
          cd backend
          dotnet restore
          
      - name: Check for vulnerabilities
        run: |
          cd backend
          dotnet list package --vulnerable --include-transitive
          
      - name: Run Snyk
        uses: snyk/actions/dotnet@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --file=backend/MySolution.sln

From my experience, automating security scans in CI/CD catches issues before they reach production. I've caught several vulnerabilities in pull requests that would have otherwise slipped through.

My Azure DevOps Pipeline

When I work with Azure DevOps, here's the pipeline I use:

trigger:
  branches:
    include:
      - main
      - develop

stages:
  - stage: SecurityScan
    displayName: 'Security Vulnerability Scan'
    jobs:
      - job: FrontendScan
        displayName: 'Frontend Security Scan'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '20.x'
            displayName: 'Install Node.js'
            
          - script: |
              cd frontend
              npm ci
              npm audit --audit-level=moderate
            displayName: 'npm audit'
            
      - job: BackendScan
        displayName: 'Backend Security Scan'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: UseDotNet@2
            inputs:
              version: '8.0.x'
            displayName: 'Install .NET SDK'
            
          - script: |
              cd backend
              dotnet restore
              dotnet list package --vulnerable --include-transitive
            displayName: 'Check .NET vulnerabilities'

How I Handle False Positives

Sometimes vulnerability scanners report issues that don't actually affect my application. Here's how I deal with them:

For My npm/Yarn Projects

I create an .npmrc or add to my package.json:

{
  "audit": {
    "exclude": [
      "GHSA-xxxx-xxxx-xxxx"
    ]
  }
}

For My .NET Projects

I use NuGetAuditSuppress in my .csproj:

<PropertyGroup>
  <NuGetAudit>true</NuGetAudit>
  <NuGetAuditSuppress>CVE-2023-12345;CVE-2023-67890</NuGetAuditSuppress>
</PropertyGroup>

⚠️ Important: I always document why I'm suppressing each vulnerability. Future me (or my teammates) will thank me later!

What I've learned: only suppress vulnerabilities after thoroughly investigating them. I keep a SECURITY.md file in my repo documenting each suppression with the reasoning behind it.

My Emergency Response Workflow

When a critical vulnerability is discovered, here's exactly what I do:

1. I Assess Impact (Within 1 hour)

# I check if I'm actually using the vulnerable package
npm ls vulnerable-package
dotnet list package | findstr VulnerablePackage

2. I Take Immediate Action (Within 4 hours)

  • I apply patches or updates right away
  • If there's no patch available, I implement workarounds
  • Sometimes I even temporarily disable affected features to protect my users

In my experience, it's better to temporarily take a feature offline than to leave a critical vulnerability exposed. I've done this twice, and users always understood when we explained the security risk.

3. I Deploy the Fix (Within 24 hours)

# Frontend
cd frontend
npm update vulnerable-package
npm audit
npm run build
npm test

# Backend
cd backend
dotnet add package VulnerablePackage --version SafeVersion
dotnet build --no-incremental
dotnet test

4. I Verify the Fix Works

# I verify no vulnerabilities remain in my project
npm audit
dotnet list package --vulnerable --include-transitive

5. I Do a Post-Incident Review

  • I document what happened and what I learned
  • I update my security policies based on lessons learned
  • I improve my monitoring to catch similar issues faster next time

From what I've learned, every security incident is a learning opportunity. I keep a private incident log where I document what went wrong and how I fixed it - it's been invaluable for improving my processes.

Conclusion

Maintaining secure React and .NET applications requires ongoing vigilance and a systematic approach. Here's what I've learned from my experience building production applications:

My Key Takeaways

  1. Regular Scanning: I run vulnerability scans at least weekly - usually every Monday morning with my coffee
  2. Automated Monitoring: I use tools like Dependabot, Snyk, and GitHub Advanced Security so I don't miss anything
  3. Quick Response: I have a clear plan for addressing critical vulnerabilities fast
  4. Dependency Hygiene: I keep my dependencies up-to-date and minimize unnecessary packages
  5. CI/CD Integration: I've made security scanning part of my build pipeline so issues are caught early

My Recommended Tools Summary

Tool Best For Cost
npm audit Quick React scans Free
dotnet list package .NET vulnerability checks Free
Snyk Comprehensive scanning Free tier available
GitHub Dependabot Automated updates Free with GitHub
OWASP Dependency-Check Deep analysis Free
WhiteSource/Mend Enterprise solutions Paid

What I Recommend You Do Next

  1. Run your first vulnerability scan today - seriously, do it right now!
  2. Set up automated scanning in your CI/CD pipeline (it took me about 30 minutes)
  3. Create a security response plan for your team
  4. Schedule regular dependency updates in your calendar
  5. Subscribe to security advisories for your stack

From my experience: Security is not a one-time task but an ongoing process. By integrating these practices into your development workflow like I have, you'll significantly reduce your application's attack surface and be much better prepared when vulnerabilities are discovered.

I've been following these practices in my projects, and it's saved me from several potential security issues. The time investment is absolutely worth it!

Additional Resources That I Use

Stay secure! πŸ”’

P.S. - If you found this helpful, feel free to reach out with your own security practices. I'm always learning new ways to keep my applications safe!