Skip to content

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-4cc2-g9w2-fhf6",
4+
"modified": "2026-06-19T22:10:26Z",
5+
"published": "2026-06-19T22:10:26Z",
6+
"aliases": [],
7+
"summary": "Zeep: Server-Side Request Forgery (SSRF)",
8+
"details": "## Summary\n\nWhen parsing a WSDL or XSD document, python-zeep follows transitive references — xsd:import, xsd:include, wsdl:import, and lxml entity/DTD resolution — and will fetch http/https URLs found in those references. The Settings.forbid_external option, intended to disable this transitive remote fetching, was defined but not wired to any logic from version 4.0.0 through 4.3.2 (a regression introduced when zeep moved off defusedxml in 4.0). As a result, setting `forbid_external=True` had no effect, and applications that processed untrusted or attacker-influenced WSDL/XSD documents could be coerced into making server-side requests to arbitrary URLs (SSRF).\n\n## Impact\n\nServer-Side Request Forgery (SSRF), CWE-918.\n\nAn attacker who can supply or influence the contents of a WSDL/XSD that an application loads with zeep can embed an import/include reference (e.g. `<xsd:import schemaLocation=\"http://169.254.169.254/...\">`) pointing at an internal or otherwise sensitive endpoint. When zeep parses the document it transitively fetches that URL using the configured transport, causing the application to issue outbound requests to attacker-chosen destinations. This can be used to reach internal-only services, cloud metadata endpoints, or other hosts not directly reachable by the attacker, and may disclose response timing/behaviour.\n\nImpacted users are those who:\n\n- load WSDL/XSD documents that are untrusted or whose import targets an attacker can control, and/or\n- relied on `forbid_external=True` as a security control — in 4.0.0–4.3.2 that setting silently did nothing, so the protection users believed they had was not in effect.\n\nNote the default was (and remains) `forbid_external=False`, i.e. transitive remote fetching is permitted by default; the security defect is specifically that the opt-out control was non-functional.\n\n## Patches\n\nFixed in python-zeep 4.3.3. The forbid_external setting is now enforced: when set to True, zeep refuses to transitively fetch http/https resources via `xsd:import`, `xsd:include`, `wsdl:import`, or lxml entity/DTD resolution, raising `zeep.exceptions.ExternalReferenceForbidden`. The user-supplied entry-point WSDL/schema URL is still loaded.\n\nAffected versions: >= 4.0.0, < 4.3.3.\n\nUpgrade to 4.3.3 (or later) and set forbid_external=True when loading documents from untrusted sources:\n\n```python\nfrom zeep import Client, Settings\n\nsettings = Settings(forbid_external=True)\nclient = Client(\"https://untrusted.example/service?wsdl\", settings=settings)\n```\n\n## Workarounds\n\nIf you cannot upgrade to 4.3.3:\n\n- Do not load untrusted WSDL/XSD documents, and avoid loading WSDLs whose import/include targets can be influenced by untrusted input.\n- Vendor the schema locally: pre-fetch and review the WSDL and all of its imported schemas, then load them from local files (e.g. file:// paths) so no remote fetching occurs at parse\n time.\n- Restrict egress at the network layer: block outbound traffic from the host/process to internal ranges and metadata endpoints (e.g. 169.254.169.254, RFC1918 ranges) so SSRF attempts\n cannot reach sensitive targets.\n- Use a restrictive custom Transport: subclass the zeep transport and reject/allow-list URLs in load() so disallowed hosts are never fetched.",
9+
"severity": [
10+
{
11+
"type": "CVSS_V3",
12+
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "PyPI",
19+
"name": "zeep"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "4.0.0"
27+
},
28+
{
29+
"fixed": "4.3.3"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
],
36+
"references": [
37+
{
38+
"type": "WEB",
39+
"url": "https://github.com/mvantellingen/python-zeep/security/advisories/GHSA-4cc2-g9w2-fhf6"
40+
},
41+
{
42+
"type": "WEB",
43+
"url": "https://github.com/mvantellingen/python-zeep/commit/83eb07bc6c84d841329d4f88856fecdba86f753e"
44+
},
45+
{
46+
"type": "PACKAGE",
47+
"url": "https://github.com/mvantellingen/python-zeep"
48+
},
49+
{
50+
"type": "WEB",
51+
"url": "https://github.com/mvantellingen/python-zeep/releases/tag/4.3.3"
52+
}
53+
],
54+
"database_specific": {
55+
"cwe_ids": [
56+
"CWE-918"
57+
],
58+
"severity": "MODERATE",
59+
"github_reviewed": true,
60+
"github_reviewed_at": "2026-06-19T22:10:26Z",
61+
"nvd_published_at": null
62+
}
63+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-4xgf-cpjx-pc3j",
4+
"modified": "2026-06-19T22:10:42Z",
5+
"published": "2026-06-19T22:10:42Z",
6+
"aliases": [],
7+
"summary": "pydantic-settings: NestedSecretsSettingsSource follows symlinks outside secrets_dir, enabling local file read and bypassing secrets_dir_max_size",
8+
"details": "### Summary\n\n`NestedSecretsSettingsSource` reads secret values from files in a configured `secrets_dir`. When `secrets_nested_subdir=True`, a directory entry inside `secrets_dir` that is a symbolic link pointing **outside** `secrets_dir` is followed, so files outside the configured directory are read into settings values. The same code path bypasses the documented `secrets_dir_max_size` protection. An attacker or lower-privileged component able to influence entries in the configured secrets directory (for example, a writable or shared secrets mount) can turn this into an unintended local file read into settings and can defeat the advertised loading-size cap. This report does not claim network reachability by itself.\n\n### Details\n\n`NestedSecretsSettingsSource` performed two passes over `secrets_dir` using two different, inconsistent directory-traversal implementations:\n\n* The size check in `validate_secrets_path()` used `Path.glob('**/*')`, which does **not** descend into a symbolically-linked directory.\n* The loader in `load_secrets()` used `glob.iglob(f'{path}/**/*', recursive=True)` followed by `read_text()`, which **does** follow symlinked directories and reads through the link target.\n\nBecause the two passes disagreed on symlinks, a symlinked directory inside `secrets_dir` whose target lives elsewhere was invisible to the size accounting (counted as 0 bytes) while still being fully read by the loader. This produces two distinct problems:\n\n1. **Out-of-tree read (CWE-22 / CWE-59).** A symlinked directory (or file) inside `secrets_dir` that resolves outside it is followed, and the external file's contents are loaded into the corresponding settings field.\n2. **`secrets_dir_max_size` bypass (CWE-400).** The size check never sees the out-of-tree content, so the documented size cap is neither respected nor able to reject the oversized external file. A related amplification exists for cyclic in-tree symlinks, which `glob.iglob(recursive=True)` re-traverses, inflating the size accounting and the number of loaded secrets.\n\n#### Reproduction\n\nIn a clean Linux container, with a `secrets_dir` containing a symlink `secrets/db -> /path/outside` and an `outside/passwd` file of 512 bytes, while `secrets_dir_max_size=100`:\n\n```python\nfrom pydantic import BaseModel\nfrom pydantic_settings import (\n BaseSettings,\n SettingsConfigDict,\n NestedSecretsSettingsSource,\n)\n\n\nclass Db(BaseModel):\n passwd: str | None = None\n\n\nclass Settings(BaseSettings):\n model_config = SettingsConfigDict(\n secrets_dir='secrets',\n secrets_nested_subdir=True,\n secrets_dir_max_size=100, # outside/passwd is 512 bytes\n )\n db: Db = Db()\n\n @classmethod\n def settings_customise_sources(\n cls, settings_cls, init_settings, env_settings, dotenv_settings, file_secret_settings\n ):\n return (NestedSecretsSettingsSource(file_secret_settings),)\n```\n\nOn affected versions, `Settings().db.passwd` is populated with the 512-byte out-of-tree file and **no** `SettingsError` is raised, even though the file exceeds `secrets_dir_max_size`.\n\n### Impact\n\nApplications that opt into `NestedSecretsSettingsSource` with `secrets_nested_subdir=True` and load secrets from a directory whose entries can be influenced by an attacker or a lower-privileged component (for example, a writable or shared secrets mount, or a secrets directory partially populated from untrusted input) are affected. The impact is:\n\n* **Confidentiality:** files outside the configured `secrets_dir` can be read into settings values (local file read).\n* **Integrity / availability of the safeguard:** the advertised `secrets_dir_max_size` cap can be bypassed, and cyclic symlinks can inflate resource usage during loading.\n\nThe vulnerability requires the ability to place a symbolic link inside the configured secrets directory; it is not remotely reachable on its own. Applications that do not use `NestedSecretsSettingsSource`, or that point `secrets_dir` at a directory fully under the application's control, are not affected.\n\n### Mitigation\n\nUpgrade to **pydantic-settings 2.14.2**, which:\n\n* walks the secrets directory explicitly and only descends into directories whose resolved path stays within `secrets_dir`, so symlinked directories pointing outside are never followed;\n* uses a single, cycle-safe iterator for both the size check and the loader, so the size accounting and the loaded set are always consistent and each real directory is visited at most once;\n* skips any file whose resolved path escapes `secrets_dir`, as defense in depth.\n\nIf upgrading is not immediately possible, ensure the configured `secrets_dir` is fully owned and controlled by the application (no writable or attacker-influenced entries), or avoid `secrets_nested_subdir=True`.",
9+
"severity": [
10+
{
11+
"type": "CVSS_V3",
12+
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "PyPI",
19+
"name": "pydantic-settings"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "2.12.0"
27+
},
28+
{
29+
"fixed": "2.14.2"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
],
36+
"references": [
37+
{
38+
"type": "WEB",
39+
"url": "https://github.com/pydantic/pydantic-settings/security/advisories/GHSA-4xgf-cpjx-pc3j"
40+
},
41+
{
42+
"type": "PACKAGE",
43+
"url": "https://github.com/pydantic/pydantic-settings"
44+
}
45+
],
46+
"database_specific": {
47+
"cwe_ids": [
48+
"CWE-22",
49+
"CWE-400",
50+
"CWE-59"
51+
],
52+
"severity": "MODERATE",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-06-19T22:10:42Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-869j-r97x-hx2g",
4+
"modified": "2026-06-19T22:10:57Z",
5+
"published": "2026-06-19T22:10:57Z",
6+
"aliases": [],
7+
"summary": "Anki's local HTTP server does not sufficiently validate requests",
8+
"details": "## Summary\n\nAnki launches a local HTTP server to serve media files and web pages for parts of its interface. The server fails to validate requests in the following ways:\n1. No sufficient validation of the Origin header.\n2. Some endpoints are vulnerable to path traversal attacks.\n\nThis allows malicious websites to exfiltrate local files given a known path.\n\n## Browser impact\n\nThe severity varies by browser because of Private Network Access (PNA), a newer spec that restricts web pages from making requests to localhost/local network addresses:\n\nChrome/Chromium (including Edge, Brave): Largely protected, as Chrome has implemented PNA restrictions for several years and now puts local network access behind a permission prompt.\nSafari: Hasn't implemented PNA yet, though macOS has some OS-level protections.\nFirefox: Most vulnerable — hasn't implemented PNA yet, though it's reportedly planned for Firefox 151.\n\n## Patches\n\nThe issue was fixed as of Anki 25.09.3",
9+
"severity": [
10+
{
11+
"type": "CVSS_V4",
12+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "PyPI",
19+
"name": "aqt"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "0"
27+
},
28+
{
29+
"fixed": "25.9.3"
30+
}
31+
]
32+
}
33+
],
34+
"database_specific": {
35+
"last_known_affected_version_range": "<= 25.9.2"
36+
}
37+
}
38+
],
39+
"references": [
40+
{
41+
"type": "WEB",
42+
"url": "https://github.com/ankitects/anki/security/advisories/GHSA-869j-r97x-hx2g"
43+
},
44+
{
45+
"type": "PACKAGE",
46+
"url": "https://github.com/ankitects/anki"
47+
},
48+
{
49+
"type": "WEB",
50+
"url": "https://x.com/taviso/status/2051310678800253318"
51+
}
52+
],
53+
"database_specific": {
54+
"cwe_ids": [
55+
"CWE-22",
56+
"CWE-346"
57+
],
58+
"severity": "HIGH",
59+
"github_reviewed": true,
60+
"github_reviewed_at": "2026-06-19T22:10:57Z",
61+
"nvd_published_at": null
62+
}
63+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-c3xh-98xp-6qhf",
4+
"modified": "2026-06-19T22:10:31Z",
5+
"published": "2026-06-19T22:10:31Z",
6+
"aliases": [],
7+
"summary": "githubtoplanguages: Command Injection via Issue Title in Discord Notification Workflow",
8+
"details": "### Summary\n\nA GitHub Actions workflow is vulnerable to command injection through the issue title.\n\nThe workflow is triggered when an issue is opened or closed, and it directly inserts `github.event.issue.title` into a Bash variable assignment. If an issue title contains command substitution syntax, Bash evaluates it during the workflow run.\n\n\n### Details\n\nThe vulnerable workflow is:\n\n`.github/workflows/discord-issue.yml`\n\nThe issue title is directly interpolated into a Bash script:\n\n```bash\nISSUE_TITLE=\"${{ github.event.issue.title || github.event.pull_request.title }}\"\n```\n\nBecause GitHub Actions expressions are expanded before Bash executes the script, an attacker-controlled issue title containing command substitution syntax can be evaluated by the shell.\n\nIn the original workflow, the resulting value is then included in a Discord notification payload:\n\n```bash\ncurl -H \"Content-Type: application/json\" \\\n -X POST \\\n -d \"{\\\"username\\\": \\\"GitHub Bot\\\", \\\"content\\\": \\\"${STATUS} created by **${AUTHOR}**: **${ISSUE_TITLE}**\\n🔗 ${ISSUE_URL}\\\"}\" \\\n \"$DISCORD_WEBHOOK\"\n```\n\n### PoC\n\nFor safety, I reproduced this only in my fork. I did not trigger the original repository’s Discord webhook.\n\nI kept the vulnerable Bash assignment unchanged and replaced the Discord webhook request with `echo` statements to observe the result safely.\n\nTest issue title:\n\n```text\ntitle: $(whoami)\n```\n\nObserved workflow log:\n\n```text\nISSUE_TITLE=title: runner\n```\n\nThis confirms that `$(whoami)` was executed on the GitHub Actions runner before the value would be sent to Discord.\n\n### Impact\n\nAny user who can open an issue may be able to execute shell commands on the GitHub Actions runner.\n\nIn practice, this means an attacker could create an issue with a crafted title, cause the workflow to execute a shell command, and have the command output included in the Discord notification content. This can be used to manipulate Discord notifications, spoof trusted GitHub bot messages, or repeatedly trigger unwanted notifications.\n\nMore importantly, the command runs in a workflow environment where a Discord webhook secret is configured. Depending on repository settings and workflow permissions, this may put workflow secrets or other environment data at risk.\n\n### Suggested Fix\n\nDo not insert issue titles directly into Bash scripts.\n\nPass the title through an environment variable instead:\n\n```yaml\nenv:\n ISSUE_TITLE: ${{ github.event.issue.title }}\nrun: |\n issue_title=\"$ISSUE_TITLE\"\n```\n\nAlso avoid `eval`, unquoted variable expansion, or shell execution patterns involving user-controlled issue content.",
9+
"severity": [
10+
{
11+
"type": "CVSS_V4",
12+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "GitHub Actions",
19+
"name": "gouef/githubtoplanguages"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "0"
27+
},
28+
{
29+
"fixed": "1.1.4"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
],
36+
"references": [
37+
{
38+
"type": "WEB",
39+
"url": "https://github.com/gouef/githubtoplanguages/security/advisories/GHSA-c3xh-98xp-6qhf"
40+
},
41+
{
42+
"type": "WEB",
43+
"url": "https://github.com/gouef/githubtoplanguages/commit/157840482e592bd4f8e0617539e73cdbef26f1ac"
44+
},
45+
{
46+
"type": "PACKAGE",
47+
"url": "https://github.com/gouef/githubtoplanguages"
48+
}
49+
],
50+
"database_specific": {
51+
"cwe_ids": [
52+
"CWE-78"
53+
],
54+
"severity": "HIGH",
55+
"github_reviewed": true,
56+
"github_reviewed_at": "2026-06-19T22:10:31Z",
57+
"nvd_published_at": null
58+
}
59+
}

0 commit comments

Comments
 (0)