imbajin commented on code in PR #514:
URL: https://github.com/apache/hugegraph-doc/pull/514#discussion_r4241694231


##########
dist/validate-site-output.py:
##########
@@ -925,6 +940,83 @@ def handle_endtag(self, tag: str) -> None:
         if tag in {"main", "article"} and self._content_depth:
             self._content_depth -= 1
 
+    def _check_component_payload(self) -> None:
+        try:
+            payload = json.loads(self._component_json)
+        except (ValueError, RecursionError):
+            self.authored_violations.append("invalid OINK component JSON 
payload")
+            return
+        if not isinstance(payload, dict):
+            self.authored_violations.append("OINK component JSON payload must 
be an object")
+            return
+        if self._component_source == "data-td-asciinema-config":
+            # Trust only the shortcode schema, not the player's much broader
+            # API (audioUrl, custom drivers, etc. can initiate other requests).
+            option_keys = {"autoPlay", "fit", "loop", "preload", "speed", 
"startAt", "poster", "cols", "rows",
+                           "idleTimeLimit", "pauseOnMarkers", "markers"}
+            options = payload.get("options")
+            if (set(payload) != {"src", "theme", "options"} or not 
isinstance(payload.get("theme"), str)
+                    or not isinstance(options, dict) or set(options) - 
option_keys):
+                self.authored_violations.append("Asciinema JSON payload does 
not match the OINK shortcode schema")
+                return
+            for key, value in options.items():
+                valid = True
+                if key in {"autoPlay", "loop", "preload", "pauseOnMarkers"}:
+                    valid = isinstance(value, bool)
+                elif key == "fit":
+                    valid = isinstance(value, str) and value in {"width", 
"height", "both", "none"}
+                elif key in {"cols", "rows"}:
+                    valid = type(value) is int and value > 0
+                elif key in {"speed", "startAt", "idleTimeLimit"}:
+                    valid = type(value) in {int, float} and value >= 0 and 
(key != "speed" or value > 0)
+                elif key == "poster":
+                    # The pinned player _parsePoster accepts terminal text or a
+                    # recording timestamp; it does not load an image/URL.
+                    valid = isinstance(value, str) and 
value.startswith(("npt:", "data:text/plain,"))
+                elif key == "markers":
+                    valid = isinstance(value, list) and all(isinstance(item, 
list) and len(item) == 2 and

Review Comment:
   🧹 Blocking: no. Summary: The validator rejects the marker shape emitted by 
the pinned Asciinema shortcode. Evidence: OINK v1.2.0 serializes 
markers="10:Boot,25:Configure" as a flat sequence, while this condition 
requires nested [time,label] pairs. Please validate the emitted flat sequence 
(or align with the pinned schema) and add a fixture based on rendered output.



##########
dist/validate-site-output.py:
##########
@@ -925,6 +940,83 @@ def handle_endtag(self, tag: str) -> None:
         if tag in {"main", "article"} and self._content_depth:
             self._content_depth -= 1
 
+    def _check_component_payload(self) -> None:
+        try:
+            payload = json.loads(self._component_json)
+        except (ValueError, RecursionError):
+            self.authored_violations.append("invalid OINK component JSON 
payload")
+            return
+        if not isinstance(payload, dict):
+            self.authored_violations.append("OINK component JSON payload must 
be an object")
+            return
+        if self._component_source == "data-td-asciinema-config":
+            # Trust only the shortcode schema, not the player's much broader
+            # API (audioUrl, custom drivers, etc. can initiate other requests).
+            option_keys = {"autoPlay", "fit", "loop", "preload", "speed", 
"startAt", "poster", "cols", "rows",
+                           "idleTimeLimit", "pauseOnMarkers", "markers"}
+            options = payload.get("options")
+            if (set(payload) != {"src", "theme", "options"} or not 
isinstance(payload.get("theme"), str)
+                    or not isinstance(options, dict) or set(options) - 
option_keys):
+                self.authored_violations.append("Asciinema JSON payload does 
not match the OINK shortcode schema")
+                return
+            for key, value in options.items():
+                valid = True
+                if key in {"autoPlay", "loop", "preload", "pauseOnMarkers"}:
+                    valid = isinstance(value, bool)
+                elif key == "fit":
+                    valid = isinstance(value, str) and value in {"width", 
"height", "both", "none"}
+                elif key in {"cols", "rows"}:
+                    valid = type(value) is int and value > 0
+                elif key in {"speed", "startAt", "idleTimeLimit"}:
+                    valid = type(value) in {int, float} and value >= 0 and 
(key != "speed" or value > 0)
+                elif key == "poster":
+                    # The pinned player _parsePoster accepts terminal text or a
+                    # recording timestamp; it does not load an image/URL.
+                    valid = isinstance(value, str) and 
value.startswith(("npt:", "data:text/plain,"))
+                elif key == "markers":
+                    valid = isinstance(value, list) and all(isinstance(item, 
list) and len(item) == 2 and
+                            type(item[0]) in {int, float} and item[0] >= 0 and 
isinstance(item[1], str) for item in value)
+                if not valid:
+                    self.authored_violations.append(f"invalid Asciinema 
shortcode option: {key}")
+            source = payload.get("src")
+            if not isinstance(source, str) or not source:
+                self.authored_violations.append("Asciinema JSON payload 
requires a source URL")
+            else:
+                self.resources.append(("asciinema-config", "src", source))
+                self.urls.append(("src", source))
+            return
+        # ECharts directly loads image:// symbols/icons and image URLs in
+        # graphic styles/rich-text backgrounds. Formatter strings can become
+        # HTML inside its default tooltip renderer and need the authored rules.
+        pending = [("", payload)]
+        while pending:
+            key, value = pending.pop()
+            if isinstance(value, dict):
+                pending.extend(value.items())
+            elif isinstance(value, list):
+                pending.extend((key, item) for item in value)
+            elif isinstance(value, str):
+                resource = value[len("image://"):] if 
value.startswith("image://") else value if key in {"image", "src", "url", 
"href"} else None

Review Comment:
   ⚠️ Blocking: yes. Summary: CSS URLs in ECharts options bypass the 
external-resource check. Evidence: The pinned ECharts bundle appends 
tooltip.extraCssText to tooltip CSS; this traversal only extracts URL-valued 
fields and formatter HTML, so background:url(https://example.org/track) is not 
added to inline_css_sources or checked by document_security_errors(). Please 
inspect CSS-bearing fields such as tooltip.extraCssText with 
css_resource_urls() and the same-origin check, and add a regression case.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to