Mostly safe β a couple of notes worth reading.
Scanned 5/13/2026, 5:06:44 AMΒ·Cached resultΒ·Deep ScanΒ·91 rulesΒ·How we decide β
AIVSS Score
Low
Severity Breakdown
0
critical
0
high
15
medium
1
low
MCP Server Information
Findings
This package scores 83/100 on safety with a B security grade, but has 15 medium-severity issues primarily around server configuration that could expose it to misuse if not properly hardened before deployment. The single low-severity finding and one insecure container image concern are manageable, though you should review the configuration recommendations before using it in production. Overall it's acceptable for use with standard security precautions, but not production-ready without addressing the server configuration gaps.
AIPer-finding remediation generated by bedrock-claude-haiku-4-5 β 16 of 16 findings. Click any finding to read.
No known CVEs found for this package or its dependencies.
Scan Details
Done
Sign in to save scan history and re-scan automatically on new commits.
Building your own MCP server?
Same rules, same LLM judges, same grade. Private scans stay isolated to your account and never appear in the public registry. Required for code your team hasnβt shipped yet.
16 of 16 findings
16 findings
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 44 | "type": "string", |
| 45 | "description": "The azure tool to use to execute the operation." |
| 46 | }, |
| 47 | "command": { |
| 48 | "type": "string", |
| 49 | "description": "The command to execute against the specified tool." |
| 50 | }, |
| 51 | "parameters": { |
| 52 | "type": "object", |
| 53 | "description": "The parameters to pass to the tool command." |
RemediationAI
The problem is that the `command` field in SingleProxyToolLoader.cs accepts an unconstrained string, allowing callers to pass arbitrary commands that could execute unintended operations. Add a JSON Schema `enum` constraint or Pydantic `Field(pattern=...)` to the `command` parameter definition to restrict it to a whitelist of known, safe command values (e.g., `enum: ["get", "set", "delete"]`). This eliminates the vulnerability by ensuring only pre-approved commands can be executed, preventing injection or privilege escalation attacks. Verify the fix by attempting to call the tool with an unlisted command value and confirming the MCP server rejects it with a schema validation error.
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 40 | "type": "string", |
| 41 | "description": "The intent of the azure operation to perform." |
| 42 | }, |
| 43 | "command": { |
| 44 | "type": "string", |
| 45 | "description": "The command to execute against the specified tool." |
| 46 | }, |
| 47 | "parameters": { |
| 48 | "type": "object", |
| 49 | "description": "The parameters to pass to the tool command." |
RemediationAI
The problem is that the `command` field in ServerToolLoader.cs accepts an unconstrained string, allowing arbitrary command injection. Modify the tool schema definition to add a `pattern` or `enum` constraint on the `command` fieldβfor example, `"pattern": "^[a-zA-Z0-9_-]+$"` or `"enum": ["deploy", "validate", "rollback"]`βto restrict input to known safe values. This fix prevents attackers from passing malicious command strings that could exploit the tool beyond its intended scope. Test by invoking the tool with both a valid command (should succeed) and an invalid/special-character command (should fail validation).
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 19 | "type": "string", |
| 20 | "description": "The name of the service." |
| 21 | }, |
| 22 | "path": { |
| 23 | "type": "string", |
| 24 | "description": "The relative path of the service main project folder" |
| 25 | }, |
| 26 | "language": { |
| 27 | "type": "string", |
| 28 | "description": "The programming language of the service." |
RemediationAI
The problem is that the `path` field in deploy-app-topology-schema.json accepts an unconstrained string, allowing path traversal or directory escape attacks (e.g., `../../etc/passwd`). Add a `pattern` constraint to the schemaβfor example, `"pattern": "^[a-zA-Z0-9._/-]+$"` and a `maxLength` limitβto restrict paths to relative, safe directory names without parent directory references. This fix prevents attackers from accessing files outside the intended service folder hierarchy. Verify by testing with both a valid relative path (e.g., `src/main`) and a malicious path (e.g., `../../../etc`) and confirming only the valid path is accepted.
Package declares an install-time hook (npm postinstall/preinstall/prepare, setup.py cmdclass override, custom setuptools install class, or non-default pyproject build-backend). Anyone installing this package runs the hook. Confirm the hook is necessary and review its contents; prefer shipping a plain library without install-time execution.
Evidence
| 31 | "@azure/mcp-linux-x64": "^0.2.3" |
| 32 | }, |
| 33 | "scripts": { |
| 34 | "postinstall": "node ./scripts/post-install-script.js" |
| 35 | } |
| 36 | } |
RemediationAI
The problem is that the `postinstall` hook in package.json runs arbitrary Node.js code (`post-install-script.js`) whenever anyone installs the package, creating a supply-chain attack surface. Review the contents of `./scripts/post-install-script.js` to confirm it performs only essential setup (e.g., downloading prebuilt binaries); if it is not strictly necessary, remove the `postinstall` script entry from the `scripts` object entirely and document any manual setup steps in the README instead. This fix eliminates silent code execution during installation and reduces the blast radius of a compromised dependency. Verify by running `npm install` in a clean environment and confirming the package installs without executing the postinstall hook (or that the hook only performs expected, auditable operations).
Dockerfile never sets a non-root `USER` directive, so the CMD runs as root by default. Any RCE or library-level vulnerability exploited inside this container gets full privileges (MCP Top-10 R3). Add `USER <non-root>` before CMD / ENTRYPOINT in the final stage β e.g. `USER 1000`, `USER nobody`, or `USER nonroot` on distroless.
Evidence
| 1 | # Build the runtime image |
| 2 | FROM mcr.microsoft.com/dotnet/aspnet:9.0.8-bookworm-slim AS runtime |
| 3 | |
| 4 | # Add build argument for publish directory |
| 5 | ARG PUBLISH_DIR |
| 6 | |
| 7 | # Error out if PUBLISH_DIR is not set |
| 8 | RUN if [ -z "$PUBLISH_DIR" ]; then \ |
| 9 | echo "ERROR: PUBLISH_DIR build argument is required" && exit 1; \ |
| 10 | fi |
| 11 | |
| 12 | # Copy the contents of the publish directory |
| 13 | COPY ${PUBLISH_DIR} . |
| 14 | |
| 15 | # List the contents of the current directory |
| 16 | RUN ls -la |
| 17 | |
| 18 | RUN if [ ! -f "azmcp.dll" ]; then \ |
| 19 | echo "ERROR: azmcp 'azmcp.d |
RemediationAI
The problem is that the Dockerfile runs the ASP.NET application as root by default, so any RCE vulnerability or malicious library code gains full system privileges. Add a `USER 1000` (or `USER nonroot` on distroless images) directive immediately before the `CMD` or `ENTRYPOINT` statement in the runtime stage to drop privileges to a non-root user. This fix limits the blast radius of container escapes and RCE exploits to the unprivileged user's capabilities. Verify by building the image, running a container, and executing `id` inside it to confirm the process runs as UID 1000 (or the specified non-root user) rather than UID 0.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal β confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | # Azure MCP Server Installation Guide |
| 2 | |
| 3 | This guide helps AI agents and developers install and configure the Azure MCP Server for different environments. |
| 4 | |
| 5 | ## Installation Steps |
| 6 | |
| 7 | ### Configuration Setup |
| 8 | |
| 9 | The Azure MCP Server requires configuration based on the client type. Below are the setup instructions for each supported client: |
| 10 | |
| 11 | #### For VS Code Users |
| 12 | |
| 13 | **β
Recommended: Use the Azure MCP Server VS Code Extension** |
| 14 | |
| 15 | 1. Open VS Code and go to the Extensions view |
| 16 | (`Ctrl+Shift+X` on Windows/Linux |
RemediationAI
The problem is that the MCP manifest in llms-install.md does not declare any authentication mechanism (no `auth`, `authorization`, `bearer`, `oauth`, `mtls`, `apiKey`, or `token` field), making it unclear whether the server is protected and how. Add an explicit `authentication` or `auth` field to the manifest (or configuration documentation) that describes the real security modelβfor example, `"auth": "bearer"` if using JWT tokens, or `"auth": "network-layer"` if relying on firewall/TLS only. This fix enables security reviewers to audit the actual authentication implementation and identify gaps. Verify by reviewing the manifest alongside the server's actual authentication code and confirming the declared mechanism matches the implementation.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 131 | # need to point to that fork |
| 132 | # Start-Build |
| 133 | # - name: Checkout tools repo for GitHub Event Processor sources |
| 134 | # uses: actions/checkout@v3 |
| 135 | # with: |
| 136 | # repository: Azure/azure-sdk-tools |
| 137 | # path: azure-sdk-tools |
RemediationAI
The problem is that `actions/checkout@v3` is pinned to a mutable tag, allowing a maintainer compromise or tag rewrite to inject malicious code into the CI pipeline. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` (substitute the actual 40-character SHA for the v3 release you intend). This fix ensures the exact version of the action is immutable and cannot be silently replaced. Verify by running `git log --oneline .github/workflows/event-processor.yml` and confirming the SHA is recorded, then test the workflow to ensure it still executes correctly.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 33 | if: ${{ github.event_name == 'issues' && github.event.action == 'opened' }} |
| 34 | steps: |
| 35 | - name: 'Az CLI login' |
| 36 | uses: azure/login@v2 |
| 37 | with: |
| 38 | client-id: ${{ secrets.AZURE_CLIENT_ID }} |
| 39 | tenant-id: ${{ secrets.AZURE_TENANT_ID }} |
RemediationAI
The problem is that `azure/login@v2` is pinned to a mutable tag, allowing a maintainer compromise to inject malicious authentication code into your CI pipeline. Replace `uses: azure/login@v2` with `uses: azure/login@9a006f6b6ab7433fcbc2f00c0cae1ffe18577b7e # v2.1.1` (substitute the actual 40-character SHA for the v2 release). This fix locks the action to an immutable commit, preventing silent substitution of malicious code. Verify by running the workflow and confirming Azure login succeeds, then inspect the workflow file to confirm the SHA is present.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 20 | name: Handle ${{ github.event_name }} ${{ github.event.action }} event |
| 21 | runs-on: ubuntu-latest # This image is intentionally set to "latest", and not to a specific version |
| 22 | steps: |
| 23 | - uses: azure/azure-sdk-actions@main |
| 24 | with: |
| 25 | token: ${{ secrets.GITHUB_TOKEN }} |
RemediationAI
The problem is that `azure/azure-sdk-actions@main` is pinned to a mutable branch, allowing any push to `main` to alter the CI behavior without review. Replace `uses: azure/azure-sdk-actions@main` with `uses: azure/azure-sdk-actions@<40-char-SHA> # main-<date>` by resolving the current commit SHA of that branch. This fix ensures the workflow uses a specific, immutable version of the action. Verify by running the workflow and confirming it completes successfully, then check that the SHA in the workflow file matches the intended commit.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 71 | # need to point to that fork |
| 72 | # Start-Build |
| 73 | # - name: Checkout tools repo for GitHub Event Processor sources |
| 74 | # uses: actions/checkout@v3 |
| 75 | # with: |
| 76 | # repository: Azure/azure-sdk-tools |
| 77 | # path: azure-sdk-tools |
RemediationAI
The problem is that `actions/checkout@v3` is pinned to a mutable tag, allowing a maintainer compromise or tag rewrite to inject malicious code. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` (use the actual SHA for v3). This fix prevents tag rewriting from silently changing the checked-out code. Verify by running the workflow and confirming the checkout succeeds, then inspect the workflow file to confirm the SHA is recorded.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 18 | contains(github.event.check_run.name, 'Build Analyze') ) |
| 19 | steps: |
| 20 | - name: Checkout |
| 21 | uses: actions/checkout@v4 |
| 22 | with: |
| 23 | sparse-checkout: 'eng/common' |
RemediationAI
The problem is that `actions/checkout@v4` is pinned to a mutable tag, allowing a maintainer compromise to inject malicious code into the CI pipeline. Replace `uses: actions/checkout@v4` with `uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7` (substitute the actual 40-character SHA for the v4 release you intend). This fix locks the action to an immutable commit, preventing silent code substitution. Verify by running the workflow and confirming the sparse checkout succeeds, then inspect the workflow file to confirm the SHA is present.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 123 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 124 | |
| 125 | - name: Archive github event data |
| 126 | uses: actions/upload-artifact@v4 |
| 127 | if: always() |
| 128 | with: |
| 129 | name: event |
RemediationAI
The problem is that `actions/upload-artifact@v4` is pinned to a mutable tag, allowing a maintainer compromise to inject malicious code into artifact handling. Replace `uses: actions/upload-artifact@v4` with `uses: actions/upload-artifact@65462800fd760344d3fac3d1f965e23d093e5d71 # v4.3.1` (substitute the actual 40-character SHA for v4). This fix ensures the artifact upload action is immutable and cannot be silently replaced. Verify by running the workflow and confirming artifacts are uploaded successfully, then check that the SHA is recorded in the workflow file.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 52 | # need to point to that fork |
| 53 | # Start-Build |
| 54 | # - name: Checkout tools repo for GitHub Event Processor sources |
| 55 | # uses: actions/checkout@v3 |
| 56 | # with: |
| 57 | # repository: Azure/azure-sdk-tools |
| 58 | # path: azure-sdk-tools |
RemediationAI
The problem is that `actions/checkout@v3` is pinned to a mutable tag, allowing a maintainer compromise or tag rewrite to inject malicious code. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` (use the actual SHA for v3). This fix prevents tag rewriting from silently changing the checked-out code. Verify by running the workflow and confirming the checkout succeeds, then inspect the workflow file to confirm the SHA is recorded.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 155 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 156 | |
| 157 | - name: Archive github event data |
| 158 | uses: actions/upload-artifact@v4 |
| 159 | if: always() |
| 160 | with: |
| 161 | name: event |
RemediationAI
The problem is that `actions/upload-artifact@v4` is pinned to a mutable tag, allowing a maintainer compromise to inject malicious code into artifact handling. Replace `uses: actions/upload-artifact@v4` with `uses: actions/upload-artifact@65462800fd760344d3fac3d1f965e23d093e5d71 # v4.3.1` (substitute the actual 40-character SHA for v4). This fix ensures the artifact upload action is immutable and cannot be silently replaced. Verify by running the workflow and confirming artifacts are uploaded successfully, then check that the SHA is recorded in the workflow file.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable β a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 96 | LABEL_SERVICE_API_KEY: ${{ env.LABEL_SERVICE_API_KEY }} |
| 97 | |
| 98 | - name: Archive github event data |
| 99 | uses: actions/upload-artifact@v4 |
| 100 | if: always() |
| 101 | with: |
| 102 | name: event |
RemediationAI
The problem is that `actions/upload-artifact@v4` is pinned to a mutable tag, allowing a maintainer compromise to inject malicious code into artifact handling. Replace `uses: actions/upload-artifact@v4` with `uses: actions/upload-artifact@65462800fd760344d3fac3d1f965e23d093e5d71 # v4.3.1` (substitute the actual 40-character SHA for v4). This fix ensures the artifact upload action is immutable and cannot be silently replaced. Verify by running the workflow and confirming artifacts are uploaded successfully, then check that the SHA is recorded in the workflow file.
Silent error swallowing detected. An except clause that does pass or ... discards the exception with no log, no metric, and no trace. This blinds incident response and hides real failures.
Evidence
| 92 | $purgeableResources += $deletedKeyVaults |
| 93 | } |
| 94 | } |
| 95 | catch { } |
| 96 | |
| 97 | return $purgeableResources |
| 98 | } |
RemediationAI
The problem is that the bare `catch { }` block silently swallows all exceptions without logging, metrics, or traces, making it impossible to detect or debug failures in resource cleanup. Replace `catch { }` with `catch { Write-Error "Error purging resources: $_"; }` or log to a monitoring system to ensure exceptions are visible. This fix enables incident response teams to detect and investigate failures in the resource purge operation. Verify by intentionally triggering an error in the try block (e.g., by passing invalid input) and confirming the error message appears in the script output or logs.