From 423489e6f668037a89407f5518b56fc4bdd17073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Andr=C3=A9s=20P=C3=A9rez=20Batanero?= Date: Wed, 3 Dec 2025 14:25:10 +0100 Subject: [PATCH 1/3] Revert "build-request: sanitize incoming "supported devices" list" This reverts commit 1579236ca291a13b1f9f8b2e2552a776821e642d. --- asu/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asu/util.py b/asu/util.py index 0c17239a..9a171409 100644 --- a/asu/util.py +++ b/asu/util.py @@ -561,7 +561,7 @@ def reload_profiles(app: FastAPI, version: str, target: str) -> bool: ) app.profiles[version][target] = { - name.replace(",", "_"): profile + name: profile for profile, data in response.json()["profiles"].items() for name in data.get("supported_devices", []) + [profile] } From d7fd8284c4da8e8e8e31976b8747b21986a74c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Andr=C3=A9s=20P=C3=A9rez=20Batanero?= Date: Wed, 3 Dec 2025 14:27:00 +0100 Subject: [PATCH 2/3] Revert "build-request: sanitize profile before use" This reverts commit 2e9edfd62ed5b079348b1e27c8f19024603d0420. --- asu/routers/api.py | 3 --- asu/util.py | 2 +- tests/test_api.py | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/asu/routers/api.py b/asu/routers/api.py index b1d0a447..700bdc66 100644 --- a/asu/routers/api.py +++ b/asu/routers/api.py @@ -209,9 +209,6 @@ def api_v1_build_post( request: Request, user_agent: str = Header(None), ): - # Sanitize the profile in case the client did not (bug in older LuCI app). - build_request.profile = build_request.profile.replace(",", "_") - add_build_event("requests") request_hash: str = get_request_hash(build_request) diff --git a/asu/util.py b/asu/util.py index 9a171409..0693f6c3 100644 --- a/asu/util.py +++ b/asu/util.py @@ -168,7 +168,7 @@ def get_request_hash(build_request: BuildRequest) -> str: build_request.version, build_request.version_code, build_request.target, - build_request.profile, + build_request.profile.replace(",", "_"), get_packages_hash( build_request.packages_versions.keys() or build_request.packages ), diff --git a/tests/test_api.py b/tests/test_api.py index fb62343c..42f35a35 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -537,7 +537,7 @@ def test_api_build_real_ath79(app): target="ath79/generic", version="23.05.5", packages=["tmux", "vim"], - profile="8dev,carambola2", # Test unsanitized profile. + profile="8dev_carambola2", ), ) From e8bf73ffcf72fd816a543f70773faf4e1c711a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Andr=C3=A9s=20P=C3=A9rez=20Batanero?= Date: Fri, 5 Dec 2025 21:10:56 +0100 Subject: [PATCH 3/3] profiles: improve SUPPORTED_DEVICES handling and profile identification The actual handling does not allow to several devices share a DTS compatible string to allow image compatibility between them. Take the lists as lists and select the profile after checking all possible candidates. Throw a new validation exception when it is not possible to identify a profile which will help to identify inconsistencies. This also can help to make a little bit clearer the difference between "profile" and "DTS compatible string". * There is not need to add [profile] to SUPPORTED_DEVICES list anymore. Before: [1] 'glinet,gl-mt2500': 'glinet_gl-mt2500-airoha', 'glinet,mt2500-emmc': 'glinet_gl-mt2500-airoha', 'glinet,gl-mt2500-airoha': 'glinet_gl-mt2500-airoha', 'glinet_gl-mt2500': 'glinet_gl-mt2500', 'glinet_gl-mt2500-airoha': 'glinet_gl-mt2500-airoha' After: 'glinet_gl-mt2500': ['glinet,gl-mt2500', 'glinet,mt2500-emmc', 'glinet,gl-mt2500-airoha', 'glinet_gl-mt2500'], 'glinet_gl-mt2500-airoha': ['glinet,gl-mt2500-airoha', 'glinet,mt2500-emmc', 'glinet,gl-mt2500', 'glinet_gl-mt2500-airoha'], [1]: https://github.com/openwrt/asu/issues/1525#issuecomment-3497242570 Fixes: https://github.com/openwrt/asu/issues/1525 Fixes: https://github.com/openwrt/openwrt/issues/20566 --- asu/routers/api.py | 40 +++++++++++++++++++++++++++++++++++++--- asu/util.py | 3 +-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/asu/routers/api.py b/asu/routers/api.py index 700bdc66..0e5c3f10 100644 --- a/asu/routers/api.py +++ b/asu/routers/api.py @@ -120,6 +120,11 @@ def valid_profile(profile: str, build_request: BuildRequest) -> bool: profiles = app.profiles[build_request.version][build_request.target] if profile in profiles: return True + else: + for supported_devices in profiles.values(): + if profile in supported_devices: + return True + if len(profiles) == 1 and "generic" in profiles: # Handles the x86, armsr and other generic variants. build_request.profile = "generic" @@ -135,9 +140,38 @@ def valid_profile(profile: str, build_request: BuildRequest) -> bool: "forums for more information." ) - build_request.profile = app.profiles[build_request.version][build_request.target][ - build_request.profile - ] + # Translate DTS compatible strings to actual profile names: + # *Only firmware-selector client is suppose to send the profile directly, + # *translate if it is not an already translated profile + if build_request.profile not in app.profiles[build_request.version][build_request.target]: + first_occurrence = 0 + profiles_appearances: dict[str, int] = dict() + for profile, supported_devices in app.profiles[build_request.version][build_request.target].items(): + if build_request.profile in supported_devices: + profiles_appearances[profile] = supported_devices.index(build_request.profile) + + if len(profiles_appearances) == 1: + build_request.profile = profiles_appearances[0] + else: + # If there are multiple profiles including the requested DTS compatible string, + # select the one where it is the first in the list of SUPPORTED_DEVICES + logging.info( + f"DTS Compatible string: {build_request.profile} appears in " + f"multiple profiles, included in: {profiles_appearances}" + ) + for profile, index in profiles_appearances.items(): + if index == 0: + first_occurrence += 1 + build_request.profile = profile + if first_occurrence != 1: + return validation_failure( + f"Identification profile problem: {build_request.profile}. " + "The requested DTS compatible string has more than one " + "profile including it as the first SUPPORTED_DEVICES. " + "Or it is not the first in the list for any profile. " + f"{profiles_appearances} Please check the forums for more information." + ) + return ({}, None) diff --git a/asu/util.py b/asu/util.py index 0693f6c3..905976de 100644 --- a/asu/util.py +++ b/asu/util.py @@ -561,9 +561,8 @@ def reload_profiles(app: FastAPI, version: str, target: str) -> bool: ) app.profiles[version][target] = { - name: profile + profile: data.get("supported_devices", []) for profile, data in response.json()["profiles"].items() - for name in data.get("supported_devices", []) + [profile] } return True