Mostly safe โ a couple of notes worth reading.
Scanned 5/3/2026, 7:05:35 PMยทCached resultยทFast Scanยท45 rulesยทHow we decide โ
AIVSS Score
Low
Severity Breakdown
0
critical
0
high
100
medium
5
low
MCP Server Information
Findings
This package has a security grade of B and a safety score of 56/100, indicating moderate risk. It contains 100 medium-severity findings, primarily related to server configuration and vulnerable dependencies, which could expose the application to potential attacks. While there are no critical or high-severity issues, the significant number of medium-severity findings warrants careful consideration before installation.
Dependencies
com.unity.test-framework (1)
com.unity.modules.uielements (1)
Scan Details
Want deeper analysis?
Fast scan found 20 findings using rule-based analysis. Upgrade for LLM consensus across 5 judges, AI-generated remediation, and cross-file taint analysis.
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.
20 of 20 findings
20 findings
com.unity.modules.uielements==1.0.0 has 1 known CVE [MEDIUM]: MAL-2022-2084. Upgrade to a patched version.
Remediation
Upgrade the pinned dependency to a patched version. Check the CVE's advisory URL for the recommended safe release, or use `npm audit fix` / `pip-audit --fix`. If no patched release is available yet, pin to a known-good prior version, vendor the fix, or remove the dependency.
com.unity.test-framework==1.1.33 has 1 known CVE [MEDIUM]: MAL-2025-17365. Upgrade to a patched version.
Remediation
Upgrade the pinned dependency to a patched version. Check the CVE's advisory URL for the recommended safe release, or use `npm audit fix` / `pip-audit --fix`. If no patched release is available yet, pin to a known-good prior version, vendor the fix, or remove the dependency.
com.unity.test-framework==1.1.33 last released 1889 days ago (>730d) โ possible abandoned package
Remediation
Typosquat: verify you meant the popular package. If so, correct the spelling; if you truly intended the less-common name, suppress with an inline waiver. Stale release: check whether the package has a maintained fork or successor. If no patched release exists, vendor the code or migrate to an active alternative before the unmaintained code accrues unfixed CVEs.
com.unity.modules.uielements==1.0.0 last released 1658 days ago (>730d) โ possible abandoned package
Remediation
Typosquat: verify you meant the popular package. If so, correct the spelling; if you truly intended the less-common name, suppress with an inline waiver. Stale release: check whether the package has a maintained fork or successor. If no patched release exists, vendor the code or migrate to an active alternative before the unmaintained code accrues unfixed CVEs.
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 | # See https://docs.microsoft.com/aspnet/core/host-and-deploy/docker/building-net-docker-images |
| 2 | # for more information about using .NET with Docker. |
| 3 | |
| 4 | # Build stage |
| 5 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build |
| 6 | WORKDIR /src |
| 7 | |
| 8 | # Copy main project file |
| 9 | COPY ["com.IvanMurzak.Unity.MCP.Server.csproj", "."] |
| 10 | |
| 11 | # Restore dependencies |
| 12 | RUN dotnet restore "com.IvanMurzak.Unity.MCP.Server.csproj" |
| 13 | |
| 14 | # Copy the rest of the source code |
| 15 | COPY . . |
| 16 | |
| 17 | # Publish |
| 18 | RUN dotnet publish "com.IvanMurzak.Unity.MCP.Server.csproj |
Remediation
Create and switch to a non-root user before the CMD / ENTRYPOINT: RUN adduser --system --uid 1000 app USER 1000 Or reuse the base image's shipped non-root account (e.g. `USER nobody`, `USER nonroot` on distroless). Multi-stage builds only need the USER directive in the final stage.
User-controlled value printed to terminal without ANSI escape sanitization. Malicious input can inject cursor-control sequences, rewrite earlier output, or hide shell commands from the operator.
Evidence
| 613 | // version: '0.67.0', // optional โ defaults to latest from OpenUPM |
| 614 | onProgress: (event) => { |
| 615 | // phase is one of: 'start' | 'dependencies-resolved' | 'manifest-patched' | 'done' |
| 616 | console.log(event.phase, event.message); |
| 617 | }, |
| 618 | }); |
Remediation
Strip C0/C1 control codes before printing user-controlled values. Python: re.sub(r"[\x00-\x08\x0b-\x1f\x7f]", "", s). Prefer a structured logger (json/logfmt) over raw print to stdout.
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
| 149 | "com.IvanMurzak.ReflectorNet.Model.ViewQuery": { |
| 150 | "type": "object", |
| 151 | "properties": { |
| 152 | "Path": { |
| 153 | "type": "string", |
| 154 | "description": "Navigate to this path first, then serialize only that subtree. Path segments are separated by '/'. Use '[i]' for array/list index (e.g. 'users/[2]/name') and '[key]' for dictionary entry (e.g. 'config/[timeout]'). A leading '#/' is stripped automatically. Examples: 'admin/name', 'users/[0]/email', 'config/[timeout]'. Leave null to |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 99 | "com.IvanMurzak.ReflectorNet.Model.ViewQuery": { |
| 100 | "type": "object", |
| 101 | "properties": { |
| 102 | "Path": { |
| 103 | "type": "string", |
| 104 | "description": "Navigate to this path first, then serialize only that subtree. Path segments are separated by '/'. Use '[i]' for array/list index (e.g. 'users/[2]/name') and '[key]' for dictionary entry (e.g. 'config/[timeout]'). A leading '#/' is stripped automatically. Examples: 'admin/name', 'users/[0]/email', 'config/[timeout]'. Leave null to |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 48 | "openedSceneName": { |
| 49 | "type": "string" |
| 50 | }, |
| 51 | "path": { |
| 52 | "type": "string" |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | ``` |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 128 | "RootCount": { |
| 129 | "type": "integer" |
| 130 | }, |
| 131 | "path": { |
| 132 | "type": "string", |
| 133 | "description": "Path to the Scene within the project. Starts with 'Assets/'" |
| 134 | }, |
| 135 | "buildIndex": { |
| 136 | "type": "integer", |
| 137 | "description": "Build index of the Scene in the Build Settings." |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 69 | "type": "integer", |
| 70 | "description": "instanceID of the UnityEngine.Object. If it is '0' and 'path', 'name', 'assetPath' and 'assetGuid' is not provided, empty or null, then it will be used as 'null'. Priority: 1 (Recommended)" |
| 71 | }, |
| 72 | "path": { |
| 73 | "type": "string", |
| 74 | "description": "Path of a GameObject in the hierarchy Sample 'character/hand/finger/particle'. Priority: 2." |
| 75 | }, |
| 76 | "name": { |
| 77 | "type": "string", |
| 78 | "description": "Name o |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 55 | "type": "integer", |
| 56 | "description": "instanceID of the UnityEngine.Object. If it is '0' and 'path', 'name', 'assetPath' and 'assetGuid' is not provided, empty or null, then it will be used as 'null'. Priority: 1 (Recommended)" |
| 57 | }, |
| 58 | "path": { |
| 59 | "type": "string", |
| 60 | "description": "Path of a GameObject in the hierarchy Sample 'character/hand/finger/particle'. Priority: 2." |
| 61 | }, |
| 62 | "name": { |
| 63 | "type": "string", |
| 64 | "description": "Name o |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 302 | "instanceID": { |
| 303 | "type": "integer" |
| 304 | }, |
| 305 | "path": { |
| 306 | "type": "string" |
| 307 | }, |
| 308 | "name": { |
| 309 | "type": "string" |
| 310 | }, |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 119 | "type": "integer", |
| 120 | "description": "instanceID of the UnityEngine.Object. If it is '0' and 'path', 'name', 'assetPath' and 'assetGuid' is not provided, empty or null, then it will be used as 'null'. Priority: 1 (Recommended)" |
| 121 | }, |
| 122 | "path": { |
| 123 | "type": "string", |
| 124 | "description": "Path of a GameObject in the hierarchy Sample 'character/hand/finger/particle'. Priority: 2." |
| 125 | }, |
| 126 | "name": { |
| 127 | "type": "string", |
| 128 | "description": "Name o |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 94 | "com.IvanMurzak.ReflectorNet.Model.ViewQuery": { |
| 95 | "type": "object", |
| 96 | "properties": { |
| 97 | "Path": { |
| 98 | "type": "string", |
| 99 | "description": "Navigate to this path first, then serialize only that subtree. Path segments are separated by '/'. Use '[i]' for array/list index (e.g. 'users/[2]/name') and '[key]' for dictionary entry (e.g. 'config/[timeout]'). A leading '#/' is stripped automatically. Examples: 'admin/name', 'users/[0]/email', 'config/[timeout]'. Leave null to |
Remediation
Shape the schema to the tool's actual intent: - Zod: chain `.enum([...])`, `.regex(/.../)`, or `.max(n)`; prefer `z.enum([...])` or `z.literal(...)` when the value set is small. - Pydantic: use `Literal["a", "b"]` or `Field(max_length=..., pattern=r"...")`. - JSON Schema: add `"enum"`, `"pattern"`, or `"maxLength"` to the property. An overbroad schema is an "overpowered tool" โ the model has nothing to prevent it from calling the tool with input far beyond what the tool's prose contract
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
| 119 | _cts = null; |
| 120 | if (cts != null) |
| 121 | { |
| 122 | try { cts.Cancel(); } catch (ObjectDisposedException) { } |
| 123 | cts.Dispose(); |
| 124 | } |
| 125 | } |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
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
| 1119 | { |
| 1120 | try |
| 1121 | { |
| 1122 | try { processToDispose.CancelOutputRead(); } catch { } |
| 1123 | try { processToDispose.CancelErrorRead(); } catch { } |
| 1124 | processToDispose.Dispose(); |
| 1125 | } |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
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
| 437 | contentToDeserialize = resultProp.GetRawText(); |
| 438 | } |
| 439 | } |
| 440 | catch { } |
| 441 | |
| 442 | var options = new JsonSerializerOptions |
| 443 | { |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
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
| 437 | contentToDeserialize = resultProp.GetRawText(); |
| 438 | } |
| 439 | } |
| 440 | catch { } |
| 441 | |
| 442 | var options = new JsonSerializerOptions |
| 443 | { |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.
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
| 1120 | try |
| 1121 | { |
| 1122 | try { processToDispose.CancelOutputRead(); } catch { } |
| 1123 | try { processToDispose.CancelErrorRead(); } catch { } |
| 1124 | processToDispose.Dispose(); |
| 1125 | } |
| 1126 | catch (Exception ex) |
Remediation
Log the exception at minimum (`logger.exception(e)`), emit a metric, or re-raise if the error is not recoverable. If you genuinely want to ignore an exception, say so with a comment.