From 59f4d5e2cf11f6b026215cced090fa69835d56c7 Mon Sep 17 00:00:00 2001 From: BELLO SHEHU <1739677116@qq.com> Date: Sun, 26 Apr 2026 16:08:50 +0000 Subject: [PATCH] fix(.life): binding ui_hints _ref fields path-traversal pattern (post-#111 review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sibling fix to #111's providers_whitelist_ref tightening. surface.ui_hints .avatar_image_ref and .background_audio_ref are also paths inside the `.life` zip and were missing the cross-schema path-traversal pattern `^(?!/)(?!.*\.\.).+$` applied elsewhere (life-package contents[].path, lifecycle.mutation_log_ref, binding.providers_whitelist_ref). Tests: 8 new cases (6 negative + 2 happy) — absolute path, parent-dir traversal, embedded `..` segment, and a normal relative path for each of the two fields. Total 55 → 63 (11 happy-path + 52 negative). Local validation: tools/test_binding_schema.py reports `run: 63 cases, failures: 0`; tools/batch_validate.py 21/21 green. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- CHANGELOG.md | 12 +++++++----- registry/index.html | 2 +- schemas/binding.schema.json | 4 ++-- tools/test_binding_schema.py | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58dbc98..581bef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,11 +79,13 @@ Sub-issues #100–#105. hybrid keyspace; `additionalProperties: false` makes unknown non-`x-` keys reject statically (decision D4=C fail-close at schema layer). [#103] -- `tools/test_binding_schema.py` — 55 sanity-test cases (9 happy-path - + 46 negative) wired into `tools/batch_validate.py`. The 55 includes - three new negatives covering `providers_whitelist_ref` path-traversal - rejection (absolute paths and `..` segments), tightening the - schema-layer defence in depth introduced after PR #111 review. [#103] +- `tools/test_binding_schema.py` — 63 sanity-test cases (11 happy-path + + 52 negative) wired into `tools/batch_validate.py`. The 63 includes + three negatives for `providers_whitelist_ref` path-traversal (added + in #111 review fix-up) and eight more cases (6 negative + 2 happy) + for path-traversal rejection on `surface.ui_hints.avatar_image_ref` + and `surface.ui_hints.background_audio_ref`, applying the same + cross-schema convention. [#103] [#101]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/101 [#102]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/102 diff --git a/registry/index.html b/registry/index.html index 7cf0e12..8b525e3 100644 --- a/registry/index.html +++ b/registry/index.html @@ -45,7 +45,7 @@

DLRS public registry

diff --git a/schemas/binding.schema.json b/schemas/binding.schema.json index 786b7e9..2522d75 100644 --- a/schemas/binding.schema.json +++ b/schemas/binding.schema.json @@ -186,8 +186,8 @@ "properties": { "disclosure_label": { "type": "string", "minLength": 1, "maxLength": 1024 }, "color_scheme": { "type": "string", "enum": ["light", "dark", "auto"] }, - "avatar_image_ref": { "type": "string", "minLength": 1 }, - "background_audio_ref": { "type": "string", "minLength": 1 } + "avatar_image_ref": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?!.*\\.\\.).+$" }, + "background_audio_ref": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?!.*\\.\\.).+$" } }, "additionalProperties": false } diff --git a/tools/test_binding_schema.py b/tools/test_binding_schema.py index ca4192e..b9cba4c 100644 --- a/tools/test_binding_schema.py +++ b/tools/test_binding_schema.py @@ -237,6 +237,20 @@ def main() -> int: g = _good_binding(); g["surface"]["ui_hints"]["unknown"] = 1 cases.append(("surface.ui_hints unknown field", g, False)) + # Path-traversal rejection on ui_hints _ref fields (cross-schema convention). + for field in ("avatar_image_ref", "background_audio_ref"): + g = _good_binding(); g["surface"]["ui_hints"][field] = "/etc/passwd" + cases.append((f"surface.ui_hints.{field} absolute path", g, False)) + + g = _good_binding(); g["surface"]["ui_hints"][field] = "../etc/passwd" + cases.append((f"surface.ui_hints.{field} parent-dir traversal", g, False)) + + g = _good_binding(); g["surface"]["ui_hints"][field] = "media/../etc/passwd" + cases.append((f"surface.ui_hints.{field} embedded `..` segment", g, False)) + + g = _good_binding(); g["surface"]["ui_hints"][field] = "media/avatar.png" + cases.append((f"surface.ui_hints.{field} normal relative path", g, True)) + g = _good_binding(); g["surface"]["unknown"] = 1 cases.append(("surface unknown field", g, False))