This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-7474-557b84a770bf150bea199aa8115cd84c1d86eb46
in repository https://gitbox.apache.org/repos/asf/texera.git

commit c7dd5ae427b2fc7a09331fa311261e1ee2a7c2c2
Author: Prateek Ganigi <[email protected]>
AuthorDate: Mon Aug 10 15:19:06 2026 -0700

    fix(workflow-operator): accept single-segment HF model IDs and reject '..' 
in the model-id check (#7474)
    
    ### What changes were proposed in this PR?
    
    The generated model-ID validation regex (`_HF_MODEL_ID_PATTERN`) had two
    problems:
    
    1. It let `..` path-traversal segments through: e.g. `org/..` passed,
    even though the comment claimed `..` was rejected (the character class
    allowed dots, so `..` was a valid segment).
    2. It rejected legacy single-segment model IDs like `gpt2` and
    `bert-base-uncased`, because it required at least one `/`.
    
    This adds a `(?!.*\.\.)` lookahead to reject any `..`, and makes the
    trailing `/segment` group optional so single-segment IDs are accepted.
    The comment and the "Invalid Hugging Face model ID" error message are
    updated to match.
    
    ### Any related issues?
    
    Closes #7196
    
    ### How was this PR tested?
    
    - Existing HuggingFace operator unit tests + the
    `PythonCodeRawInvalidTextSpec` py-compile guard (confirms the new regex
    is valid Python).
    - Extended the existing MODEL_ID spec test to assert the lookahead and
    the now-optional segment group are emitted.
    - Behavioral check of the emitted regex: `gpt2`, `bert-base-uncased`,
    `t5-small`, `org/model`, `org/model/revision` are accepted; `org/..`,
    `org/../secret`, `..` are rejected.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    This PR was co-authored with Claude in compliance with ASF policy.
---
 .../operator/huggingFace/codegen/HuggingFaceCodegenBase.scala  | 10 ++++++----
 .../operator/huggingFace/HuggingFaceInferenceOpDescSpec.scala  |  4 ++++
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/HuggingFaceCodegenBase.scala
 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/HuggingFaceCodegenBase.scala
index 9b59ce6d92..613a77aab3 100644
--- 
a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/HuggingFaceCodegenBase.scala
+++ 
b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/huggingFace/codegen/HuggingFaceCodegenBase.scala
@@ -72,9 +72,11 @@ object HuggingFaceCodegenBase {
        |
        |# Defensive format check for MODEL_ID before it is interpolated into
        |# HF URL paths. The base host is hardcoded so the worst case isn't
-       |# SSRF, but rejecting `..` segments / query strings / fragments /
-       |# control chars keeps the operator's request shape predictable.
-       |_HF_MODEL_ID_PATTERN = 
re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*(/[A-Za-z0-9._-]+)+$$")
+       |# SSRF, but rejecting `..` traversal / query strings / fragments /
+       |# control chars keeps the operator's request shape predictable. The
+       |# leading (?!.*\.\.) rejects any `..`; the trailing /segment group is
+       |# optional so single-segment legacy IDs like `gpt2` are also accepted.
+       |_HF_MODEL_ID_PATTERN = 
re.compile(r"^(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._-]*(/[A-Za-z0-9._-]+)*$$")
        |
        |class ProcessTableOperator(UDFTableOperator):
        |
@@ -488,7 +490,7 @@ object HuggingFaceCodegenBase {
        |        if not _HF_MODEL_ID_PATTERN.match(self.MODEL_ID or ""):
        |            raise ValueError(
        |                f"Invalid Hugging Face model ID '{self.MODEL_ID}'. "
-       |                f"Expected format like 'org/model-name' or 
'org/model-name/revision'."
+       |                f"Expected a model ID like 'gpt2', 'org/model-name', 
or 'org/model-name/revision'."
        |            )
        |
        |        # --- resolve API token ---
diff --git 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceInferenceOpDescSpec.scala
 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceInferenceOpDescSpec.scala
index bf6549fb80..439b111fae 100644
--- 
a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceInferenceOpDescSpec.scala
+++ 
b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/huggingFace/HuggingFaceInferenceOpDescSpec.scala
@@ -141,6 +141,10 @@ class HuggingFaceInferenceOpDescSpec extends AnyFlatSpec 
with Matchers {
     code should include("if not _HF_MODEL_ID_PATTERN.match(")
     code should include("raise ValueError(")
     code should include("Invalid Hugging Face model ID")
+    // #7196: reject `..` path traversal (leading negative lookahead) and 
accept
+    // single-segment legacy IDs like `gpt2` (trailing /segment group 
optional).
+    code should include("(?!.*\\.\\.)")
+    code should include("(/[A-Za-z0-9._-]+)*$")
   }
 
   it should "not leak raw user-input strings into the generated Python source" 
in {

Reply via email to