DotnetAngularDoctor.Tool 0.5.1

dotnet tool install --global DotnetAngularDoctor.Tool --version 0.5.1
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local DotnetAngularDoctor.Tool --version 0.5.1
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=DotnetAngularDoctor.Tool&version=0.5.1
                    
nuke :add-package DotnetAngularDoctor.Tool --version 0.5.1
                    

DotnetAngularDoctor

build NuGet license: MIT

A command line tool that reads an ASP.NET Core + Angular repository and finds the configuration mistakes that make the application work locally but fail after deployment, or fail as soon as the frontend is wired to the backend.

Status: version 0.5, static analysis only. The tool reads files. It never modifies your project, never runs your code and never talks to the network. See Safety.

The problem

The settings that decide whether a full stack app works are spread over many files, and each file looks correct on its own:

Setting Lives in
CORS policy, cookie options, SignalR hubs, SPA fallback Program.cs / Startup.cs
Backend urls Properties/launchSettings.json
Dev server port, build output angular.json
Request forwarding proxy.conf.json / .js / .ts
IIS hosting, WebSockets web.config
withCredentials Angular services and interceptors

Only the combination is wrong: AllowAnyOrigin() next to AllowCredentials(), a proxy that points at port 7240 while Kestrel listens on 7042, a hub route that the proxy does not forward, a SPA fallback that points at index.html while Angular writes wwwroot/browser/index.html. Each of these costs hours of bisecting files. This tool collects the settings, compares them, and tells you what does not line up.

Example

$ dotnet-angular-doctor scan --path samples/BrokenProxyProject

Dotnet Angular Doctor
version 0.5.1

Project discovery
-----------------
✔ ASP.NET Core project detected
    Only one ASP.NET Core project was found: src/MyApi/MyApi.csproj (web SDK ..., depth 2).
✔ Angular project detected
    Only one Angular project was found: src/web/angular.json (angular.json, @angular/core ...).
✔ Program.cs detected
    src/MyApi/Program.cs
✔ launchSettings.json detected
    https://localhost:7042, http://localhost:5042
✔ angular.json detected
✔ Angular proxy detected
    src/web/proxy.conf.json

Rules executed: 14 of 14

Diagnostics
-----------
⚠ FSD005 Angular proxy target does not match the ASP.NET Core launch URL
  File: src/web/proxy.conf.json
  Line: 2
  Confidence: High

  The Angular proxy forwards /api to https://localhost:7240, which is not one of the urls
  the backend starts on.

  Why it matters:
  During development every proxied call ends in a connection error or a 502, which usually
  looks like a broken API rather than a configuration mismatch.

  Evidence:
    Proxy target: https://localhost:7240
    Backend launch urls: https://localhost:7042, http://localhost:5042

  Suggested fix:
    Point the proxy target at one of the launch urls, for example 'https://localhost:7042',
    or change applicationUrl in launchSettings.json so both sides agree.

Summary
-------
Errors:      0
Warnings:    1
Information: 0

Scan completed in 118 ms.

Every finding carries: what, why it matters, which file, which line, the current value, the expected value, a severity, a confidence level and a text only suggested fix.

Install

From NuGet

dotnet tool install --global DotnetAngularDoctor.Tool
dotnet-angular-doctor scan --path /path/to/your/repo

Update or remove it:

dotnet tool update --global DotnetAngularDoctor.Tool
dotnet tool uninstall --global DotnetAngularDoctor.Tool

Requirements: the .NET 8 runtime or newer. The tool targets net8.0, so it runs on .NET 8, 9 and 10.

From source

git clone https://github.com/mohamedanter1996/DotnetAngularDoctor.git
cd DotnetAngularDoctor && dotnet pack src/DotnetAngularDoctor.Cli -c Release
dotnet tool install --global --add-source ./artifacts/package DotnetAngularDoctor.Tool

Prefer not to install anything? Run it straight from the repository:

dotnet run --project src/DotnetAngularDoctor.Cli -- scan --path /path/to/your/repo

Usage

dotnet-angular-doctor scan
dotnet-angular-doctor scan --path ./MySolution
dotnet-angular-doctor scan --path ./backend-repo --path ./frontend-repo
dotnet-angular-doctor scan --format json
dotnet-angular-doctor scan --format sarif --output results.sarif
dotnet-angular-doctor scan --severity warning
dotnet-angular-doctor scan --rule FSD001 --rule FSD005
dotnet-angular-doctor scan --exclude-rule FSD004
dotnet-angular-doctor scan --fail-on-error
dotnet-angular-doctor list-rules
dotnet-angular-doctor skill
dotnet-angular-doctor --version

Options

Option Default Meaning
--path, -p . Directory to scan. Point it at the repository root or the solution folder. Repeatable: pass it once per repository when the backend and the frontend are separate repositories.
--format, -f text text for humans, json for other tools, sarif for GitHub code scanning and IDEs. Machine formats go to standard output on their own.
--output, -o (stdout) Write the report to this file. json and sarif only. The only file a scan ever creates.
--severity, -s information Lowest severity to report: information, warning, error.
--rule, -r (all) Run only these rule ids. Repeatable.
--exclude-rule, -x (none) Never run these rule ids. Repeatable.
--fail-on-error off Let findings decide the exit code (see below).
--verbose, -v off Print analysis notes, the parsed files and full stack traces.
--ascii auto Plain ASCII marks instead of ✔ ○ ✖. Chosen automatically when the output is redirected, so hosts that read a legacy code page - the Visual Studio Package Manager Console among them - stay readable.
--help Full help for any command.

Separate repositories

The backend and the frontend do not have to live in one repository, and they may each have their own solution.

Layout Command
One repository scan --path ./MyApp
Two repositories under one folder scan --path ./work - both are found, because a folder that is not itself a repository is treated as a workspace
Two repositories that share nothing, for example different drives scan --path D:/backend --path E:/frontend

Everything a rule compares across the two halves - the proxy target against the launch url, the hub route against the proxy contexts, the Angular origin against the CORS policy - works the same way, because the rules read one merged model and never touch the file system themselves.

Reported paths are relative to the deepest folder that contains every scanned path, so the two repos show up as backend/... and frontend/.... Directories that are repositories of their own are only skipped when the scanned folder is itself a repository, where they can only be a worktree or a submodule.

Exit codes

Code Meaning
0 No error found, or findings were reported without --fail-on-error.
1 Warnings only. Requires --fail-on-error.
2 At least one error. Requires --fail-on-error.
3 The tool failed, or no project could be analyzed under the given path.

Documented default: without --fail-on-error a scan that finds problems still exits 0, so running the tool locally never breaks a shell script. Code 3 is returned regardless of the flag, because that means the tool could not do its job.

JSON output

The JSON contract is stable: property names never change inside a major version, and new versions may only add fields.

{
  "toolVersion": "0.5.1",
  "scannedPath": "samples/BrokenCorsProject",
  "durationMilliseconds": 132,
  "success": true,
  "failureMessage": null,
  "projects": { "dotnetProjects": 1, "angularProjects": 1 },
  "rules": { "registered": 12, "executed": 12 },
  "summary": { "errors": 1, "warnings": 0, "information": 0 },
  "diagnostics": [
    {
      "ruleId": "FSD001",
      "severity": "Error",
      "category": "CORS",
      "title": "AllowAnyOrigin cannot be combined with AllowCredentials",
      "filePath": "src/MyApi/Program.cs",
      "lineNumber": 9,
      "message": "The CORS policy 'AllowAngular' calls both AllowAnyOrigin() and AllowCredentials().",
      "whyItMatters": "Credentialed requests require explicit trusted origins.",
      "evidence": "AllowAnyOrigin() ... AllowCredentials()",
      "suggestedFix": "Replace AllowAnyOrigin with WithOrigins and provide the Angular origin.",
      "confidence": "High",
      "documentationUrl": "https://github.com/mohamedanter1996/DotnetAngularDoctor/blob/main/docs/diagnostic-rules.md#fsd001"
    }
  ],
  "notes": []
}

Rules

Fourteen rules ship in version 0.5. Full reference with a broken example, a correct example and the uncertain cases: docs/diagnostic-rules.md.

Rule Category Title
FSD001 CORS AllowAnyOrigin cannot be combined with AllowCredentials
FSD002 CORS Angular origin is not included in the configured CORS origins
FSD003 Cookies SameSite None cookie may be sent without a secure policy
FSD004 Cookies Cookie authentication detected but Angular credentials configuration was not found
FSD005 Proxy Angular proxy target does not match the ASP.NET Core launch URL
FSD006 SignalR SignalR hub route is not included in the Angular proxy
FSD007 SignalR SignalR service registration and hub mapping are inconsistent
FSD008 SPA SPA fallback route was not found
FSD009 SPA SPA fallback file may not match the Angular build output
FSD010 SignalR Angular proxy WebSocket support may be disabled
FSD011 Middleware Authentication or authorization middleware order may be invalid
FSD012 Middleware CORS middleware order may prevent the policy from being applied
FSD013 Middleware HTTPS redirection may break the calls the frontend makes over HTTP
FSD014 Proxy Angular environment points at a backend url that is not a launch url

Confidence is part of every finding. When the analysis cannot prove something - a value that comes from configuration, a setting that depends on the environment, a proxy.conf.js that would have to be executed to be understood - the tool reports a warning with lower confidence instead of a certain error.

CI/CD

GitHub Action

The repository ships an action, so findings land in the Security → Code scanning tab with the file, the line and the suggested fix, and appear as annotations on the pull request that introduced them.

name: configuration
on: [push, pull_request]

permissions:
  contents: read
  security-events: write   # required to upload the SARIF log

jobs:
  doctor:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: mohamedanter1996/DotnetAngularDoctor@v1

Every input, with its default:

      - uses: mohamedanter1996/DotnetAngularDoctor@v1
        with:
          path: '.'                                  # directory to scan
          severity: 'information'                    # information | warning | error
          rules: ''                                  # e.g. 'FSD001 FSD005', empty runs all
          exclude-rules: ''                          # e.g. 'FSD004'
          fail-on-error: 'true'                      # fail the step on findings
          sarif-file: 'dotnet-angular-doctor.sarif'  # where the log is written
          upload-sarif: 'true'                       # send it to code scanning
          tool-version: ''                           # pin the tool, empty takes the latest
          skip-install: 'false'                      # true uses a doctor already on PATH
          job-summary: 'true'                        # readable report in the job summary

Outputs: sarif-file, exit-code, errors, warnings.

Code scanning upload is free on public repositories; private repositories need GitHub Advanced Security. Set upload-sarif: false to keep the log as a plain artifact instead.

Plain commands

- name: Check full stack configuration
  run: |
    dotnet tool install --global DotnetAngularDoctor.Tool
    dotnet-angular-doctor scan --fail-on-error

Only fail on errors and keep warnings visible:

- name: Check full stack configuration (errors only)
  run: dotnet-angular-doctor scan --severity warning --fail-on-error

Keep the machine readable report as a build artifact:

- name: Configuration report
  run: dotnet-angular-doctor scan --format json --output doctor-report.json
- uses: actions/upload-artifact@v7
  with:
    name: doctor-report
    path: doctor-report.json

Any other SARIF consumer, without the action:

- run: dotnet-angular-doctor scan --format sarif --output results.sarif
- uses: github/codeql-action/upload-sarif@v4
  with:
    sarif_file: results.sarif

Azure Pipelines:

- script: dotnet-angular-doctor scan --fail-on-error
  displayName: Check full stack configuration

Pre-commit hook (.git/hooks/pre-commit):

#!/bin/sh
dotnet-angular-doctor scan --severity error --fail-on-error || exit 1

Using it with an AI assistant

The tool finds the mismatch; it never edits a file. An AI coding assistant is the other half - it can apply the fix once it knows exactly what to change. The tool ships the instructions that make that reliable, so they always match the installed version:

dotnet-angular-doctor skill

That prints a skill document - how to scan, the JSON contract, how to weigh confidence against severity, and which "fixes" are forbidden because they silence a finding by weakening the application. --fixes prints the per rule recipes.

For an assistant that loads skills from a folder:

dotnet-angular-doctor skill --install .claude/skills

For one that takes an upload instead - the Skills page of the Claude apps, for example - write the archive it expects, SKILL.md at the root and nothing else in it:

dotnet-angular-doctor skill --package dotnet-angular-doctor-skill.zip

For any other assistant, one line in whatever instruction file it already reads - AGENTS.md, CLAUDE.md, .github/copilot-instructions.md, a Cursor rule - is enough:

Before changing CORS, dev proxy, SignalR, SPA fallback or middleware order, run
`dotnet-angular-doctor skill` and follow the instructions it prints.

Full setup, including what the skill tells the assistant to do and refuse: docs/using-with-ai-agents.md.

Sample projects

The samples folder contains one healthy repository and five deliberately broken ones. They are analysis fixtures, not runnable applications, and are excluded from the solution on purpose.

dotnet run --project src/DotnetAngularDoctor.Cli -- scan --path samples/HealthyProject
dotnet run --project src/DotnetAngularDoctor.Cli -- scan --path samples/BrokenSignalRProject
Sample Expected findings
HealthyProject none
BrokenCorsProject FSD001
BrokenCookieProject FSD003, FSD004
BrokenProxyProject FSD005
BrokenSignalRProject FSD006
BrokenSpaFallbackProject FSD009

Safety and privacy

The tool works on your source code, so it is deliberately restricted:

  • It only reads files. No file of your project is created, changed or deleted.
  • There is no auto fix. Suggested fixes are text, so every edit stays yours to review.
  • No network access. Nothing is uploaded, no cloud service is used, no telemetry is collected.
  • It never executes anything from the analyzed project: no npm, no dotnet, no build, no scripts.
  • It never restores or loads the analyzed project's packages. The C# analysis is syntax only.
  • Secrets are redacted before they enter the report: passwords, keys, tokens, connection strings and similar values from appsettings*.json and environment variables are replaced with ***REDACTED***. A test asserts that no secret from the sample project can reach text or JSON output.
  • Suggested fixes never weaken security: the tool will not tell you to allow any origin, disable TLS validation or drop a secure cookie policy.

Contributing

Bug reports, rule ideas and pull requests are welcome. Start with CONTRIBUTING.md, and read docs/creating-rules.md if you want to add a rule - it takes one class and one test file, and the engine never has to change.

dotnet build DotnetAngularDoctor.sln
dotnet test DotnetAngularDoctor.sln

Roadmap

Next up are opt in runtime checks (reachability, SignalR negotiate, redirects, cookie round trips, TLS certificates, WebSocket dial, security headers). Everything that is deliberately out of scope today is listed in docs/roadmap.md.

Documentation

License

MIT.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.5.1 262 7/27/2026
0.5.0 119 7/27/2026
0.4.0 117 7/26/2026
0.3.0 119 7/26/2026
0.2.2 119 7/26/2026
0.2.1 105 7/26/2026
0.2.0 112 7/26/2026
0.1.1 124 7/26/2026