korbit-ai[bot] commented on code in PR #32656:
URL: https://github.com/apache/superset/pull/32656#discussion_r1994027766


##########
scripts/build_docker.py:
##########
@@ -0,0 +1,294 @@
+#!/usr/bin/env python3
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import os
+import re
+import subprocess
+from textwrap import dedent
+
+import click
+
+REPO = "viveksingh27/bi-superset"
+CACHE_REPO = f"{REPO}-cache"
+BASE_PY_IMAGE = "3.10-slim-bookworm"
+
+
+def run_cmd(command: str, raise_on_failure: bool = True) -> str:
+    process = subprocess.Popen(
+        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
text=True
+    )
+
+    output = ""
+    if process.stdout is not None:
+        for line in iter(process.stdout.readline, ""):
+            print(line.strip())  # Print the line to stdout in real-time
+            output += line
+
+    process.wait()  # Wait for the subprocess to finish
+
+    if process.returncode != 0 and raise_on_failure:
+        raise subprocess.CalledProcessError(process.returncode, command, 
output)
+    return output
+
+
+def get_git_sha() -> str:
+    return run_cmd("git rev-parse HEAD").strip()
+
+
+def get_build_context_ref(build_context: str) -> str:
+    """
+    Given a context, return a ref:
+    - if context is pull_request, return the PR's id
+    - if context is push, return the branch
+    - if context is release, return the release ref
+    """
+
+    event = os.getenv("GITHUB_EVENT_NAME")
+    github_ref = os.getenv("GITHUB_REF", "")
+
+    if event == "pull_request":
+        github_head_ref = os.getenv("GITHUB_HEAD_REF", "")
+        return re.sub("[^a-zA-Z0-9]", "-", github_head_ref)[:40]
+    elif event == "release":
+        return re.sub("refs/tags/", "", github_ref)[:40]
+    elif event == "push":
+        return re.sub("[^a-zA-Z0-9]", "-", re.sub("refs/heads/", "", 
github_ref))[:40]
+    return ""
+
+
+def is_latest_release(release: str) -> bool:
+    output = (
+        run_cmd(
+            f"./scripts/tag_latest_release.sh {release} --dry-run",
+            raise_on_failure=False,
+        )
+        or ""
+    )
+    return "SKIP_TAG::false" in output
+
+
+def make_docker_tag(l: list[str]) -> str:  # noqa: E741
+    return f"{REPO}:" + "-".join([o for o in l if o])
+
+
+def get_docker_tags(
+    build_preset: str,
+    build_platforms: list[str],
+    sha: str,
+    build_context: str,
+    build_context_ref: str,
+    force_latest: bool = False,
+) -> set[str]:
+    """
+    Return a set of tags given a given build context
+    """
+    tags: set[str] = set()
+    tag_chunks: list[str] = []
+
+    is_latest = is_latest_release(build_context_ref)
+
+    if build_preset != "lean":
+        # Always add the preset_build name if different from default (lean)
+        tag_chunks += [build_preset]
+
+    if len(build_platforms) == 1:
+        build_platform = build_platforms[0]
+        short_build_platform = build_platform.replace("linux/", 
"").replace("64", "")
+        if short_build_platform != "amd":
+            # Always a platform indicator if different from default (amd)
+            tag_chunks += [short_build_platform]
+
+    # Always craft a tag for the SHA
+    tags.add(make_docker_tag([sha] + tag_chunks))
+    # also a short SHA, cause it's nice
+    tags.add(make_docker_tag([sha[:7]] + tag_chunks))
+
+    if build_context == "release":
+        # add a release tag
+        tags.add(make_docker_tag([build_context_ref] + tag_chunks))
+        if is_latest or force_latest:
+            # add a latest tag
+            tags.add(make_docker_tag(["latest"] + tag_chunks))
+    elif build_context == "push" and build_context_ref == "master":
+        tags.add(make_docker_tag(["master"] + tag_chunks))
+    elif build_context == "pull_request":
+        tags.add(make_docker_tag([f"pr-{build_context_ref}"] + tag_chunks))
+    return tags
+
+
+def get_docker_command(
+    build_preset: str,
+    build_platforms: list[str],
+    is_authenticated: bool,
+    sha: str,
+    build_context: str,
+    build_context_ref: str,
+    force_latest: bool = False,
+) -> str:
+    tag = ""  # noqa: F841
+    build_target = ""
+    py_ver = BASE_PY_IMAGE
+    docker_context = "."
+
+    if build_preset == "dev":
+        build_target = "dev"
+    elif build_preset == "lean":
+        build_target = "lean"
+    elif build_preset == "py311":
+        build_target = "lean"
+        py_ver = "3.11-slim-bookworm"
+    elif build_preset == "websocket":
+        build_target = ""
+        docker_context = "superset-websocket"
+    elif build_preset == "ci":
+        build_target = "ci"
+    elif build_preset == "dockerize":
+        build_target = ""
+        docker_context = "-f dockerize.Dockerfile ."
+    else:
+        print(f"Invalid build preset: {build_preset}")
+        exit(1)
+
+    # Try to get context reference if missing
+    if not build_context_ref:
+        build_context_ref = get_build_context_ref(build_context)
+
+    tags = get_docker_tags(
+        build_preset,
+        build_platforms,
+        sha,
+        build_context,
+        build_context_ref,
+        force_latest,
+    )
+    docker_tags = ("\\\n" + 8 * " ").join([f"-t {s} " for s in tags])
+
+    docker_args = "--load" if not is_authenticated else "--push"
+    target_argument = f"--target {build_target}" if build_target else ""
+
+    cache_ref = f"{CACHE_REPO}:{py_ver}"
+    if len(build_platforms) == 1:
+        build_platform = build_platforms[0]
+        short_build_platform = build_platform.replace("linux/", 
"").replace("64", "")
+        cache_ref = f"{CACHE_REPO}:{py_ver}-{short_build_platform}"
+    platform_arg = "--platform " + ",".join(build_platforms)
+
+    cache_from_arg = f"--cache-from=type=registry,ref={cache_ref}"
+    cache_to_arg = (
+        f"--cache-to=type=registry,mode=max,ref={cache_ref}" if 
is_authenticated else ""
+    )
+    build_arg = f"--build-arg PY_VER={py_ver}" if py_ver else ""
+    actor = os.getenv("GITHUB_ACTOR")
+
+    return dedent(
+        f"""\
+        docker buildx build \\
+        {docker_args} \\
+        {docker_tags} \\
+        {cache_from_arg} \\
+        {cache_to_arg} \\
+        {build_arg} \\
+        {platform_arg} \\
+        {target_argument} \\
+        --label sha={sha} \\
+        --label target={build_target} \\
+        --label build_trigger={build_context} \\
+        --label base={py_ver} \\
+        --label build_actor={actor} \\
+        {docker_context}"""
+    )
+
+
[email protected]()
[email protected](
+    "build_preset",
+    type=click.Choice(["lean", "dev", "dockerize", "websocket", "py311", 
"ci"]),
+)
[email protected]("build_context", type=click.Choice(["push", "pull_request", 
"release"]))
[email protected](
+    "--platform",
+    type=click.Choice(["linux/arm64", "linux/amd64"]),
+    default=["linux/amd64"],
+    multiple=True,
+)
[email protected]("--build_context_ref", help="a reference to the pr, release or 
branch")
[email protected]("--dry-run", is_flag=True, help="Run the command in dry-run 
mode.")
[email protected]("--verbose", is_flag=True, help="Print more info")
[email protected](
+    "--force-latest", is_flag=True, help="Force the 'latest' tag on the 
release"
+)
+def main(
+    build_preset: str,
+    build_context: str,
+    build_context_ref: str,
+    platform: list[str],
+    dry_run: bool,
+    force_latest: bool,
+    verbose: bool,
+) -> None:
+    """
+    This script executes docker build and push commands based on given 
arguments.
+    """
+
+    is_authenticated = (
+        True if os.getenv("DOCKERHUB_TOKEN") and os.getenv("DOCKERHUB_USER") 
else False
+    )
+
+    if force_latest and build_context != "release":
+        print(
+            "--force-latest can only be applied if the build context is set to 
'release'"
+        )
+        exit(1)
+
+    if build_context == "release" and not build_context_ref.strip():
+        print("Release number has to be provided")
+        exit(1)
+
+    docker_build_command = get_docker_command(
+        build_preset,
+        platform,
+        is_authenticated,
+        get_git_sha(),
+        build_context,
+        build_context_ref,
+        force_latest,
+    )
+
+    if not dry_run:
+        print("Executing Docker Build Command:")
+        print(docker_build_command)
+        script = ""
+        if os.getenv("DOCKERHUB_USER"):
+            script = dedent(
+                f"""\
+                docker logout
+                docker login --username "{os.getenv("DOCKERHUB_USER")}" 
--password "{os.getenv("DOCKERHUB_TOKEN")}"

Review Comment:
   ### Exposed Docker Credentials in Command Line <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Docker credentials are being passed directly as command line arguments, 
which can expose them in process listings and command history.
   
   ###### Why this matters
   Command line arguments can be viewed by other users on the system via ps, 
history, or similar commands, potentially exposing sensitive credentials.
   
   ###### Suggested change ∙ *Feature Preview*
   Use stdin or docker config for credential input:
   ```python
   run_cmd(f"echo $DOCKERHUB_TOKEN | docker login --username $DOCKERHUB_USER 
--password-stdin")
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/2a1afc5e-708b-4993-b300-dffbf8d4fe0e?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:cda1ffdd-b056-42c0-b569-0592642b40c8 -->
   



##########
superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx:
##########
@@ -76,28 +96,159 @@ export const HandlebarsViewer = ({
 };
 
 //  usage: {{dateFormat my_date format="MMMM YYYY"}}
-Handlebars.registerHelper('dateFormat', function (context, block) {
+hb.registerHelper('dateFormat', function (context: any, block: any) {
   const f = block.hash.format || 'YYYY-MM-DD';
   return moment(context).format(f);
 });
 
+hb.registerHelper('parseJSON', (jsonString: string) => {
+  if (jsonString === undefined)
+    throw Error('Please call with an object. Example: `parseJSON myObj`');
+  return JSON.parse(jsonString);
+});
+
+hb.registerHelper('inc', (value: string) => {
+  if (value === undefined)
+    throw Error('Please call with an object. Example: `inc @index`');
+  return parseInt(value, 10) + 1;
+});
+
 // usage: {{  }}
-Handlebars.registerHelper('stringify', (obj: any, obj2: any) => {
+hb.registerHelper('stringify', (obj: any, obj2: any) => {
   // calling without an argument
   if (obj2 === undefined)
     throw Error('Please call with an object. Example: `stringify myObj`');
   return isPlainObject(obj) ? JSON.stringify(obj) : String(obj);
 });
 
-Handlebars.registerHelper(
-  'formatNumber',
-  function (number: any, locale = 'en-US') {
-    if (typeof number !== 'number') {
-      return number;
+hb.registerHelper('formatNumber', function (number: any, locale = 'en-US') {
+  if (typeof number !== 'number') {
+    return number;
+  }
+  return number.toLocaleString(locale);
+});
+
+HandlebarsGroupBy.register(hb);
+
+// Example async function to fetch presigned URLs
+async function fetchPresignedUrls0(imageLinks: string[]): Promise<string[]> {
+  const response = await fetch('/api/v1/presigned_urls/', {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ image_links: imageLinks }),
+  });
+
+  if (response.ok) {
+    const data = await response.json();
+    return data.presigned_urls;
+  }
+  console.error('Error fetching presigned URLs');
+  return [];
+}
+
+// Register async helper
+hb.registerHelper('getPresignedUrls', async (imageLinks: string[]) => {
+  const urls = await fetchPresignedUrls0(imageLinks);
+  const urls1 = JSON.parse(urls as unknown as string);
+  const presignedUrls = urls1.map(
+    (item: { presigned_url: string }) => item.presigned_url,
+  );
+  return presignedUrls;
+});
+
+hb.registerHelper('jsonToHtmlTable', (jsonData1: string, jsonData2: string) => 
{
+  const assignmentData = JSON.parse(jsonData1);
+  const reviewData = JSON.parse(jsonData2);
+  const items = assignmentData.items || [];
+  const errors = reviewData.diff || [];
+  let html = '';
+
+  let Total = assignmentData.total || '';
+  const Date = assignmentData.txn ? assignmentData.txn.date || '' : '';
+  const Hour = assignmentData.txn ? assignmentData.txn.hour || '' : '';
+  const Minute = assignmentData.txn ? assignmentData.txn.minute || '' : '';
+  const Suffix = assignmentData.txn ? assignmentData.txn.suffix || '' : '';
+  let Channel = assignmentData.channel || '';
+
+  if (items) {
+    errors.forEach((error: any[][]) => {
+      if (error[1][0] === 'total') {
+        Total += `<br><span class="is_error">${error[2][0]}</span>`; // Add 
the incorrect original quantity
+      } else if (error[1][0] === 'channel') {
+        Channel += `<br><span class="is_error">${error[2][0]}</span>`; // Add 
the incorrect original quantity
+      }
+    });
+
+    html +=
+      '<h3>Summary</h3><table id="receipt_summary_table" 
class="pretty_table"><tbody>';
+    html += `<tr><td>Total</td><td>${Total}</td></tr>
+           <tr><td>Transaction Date</td><td>${Date}</td></tr>
+           <tr><td>Transaction 
Time</td><td>${Hour}:${Minute}:${Suffix}</td></tr>
+           <tr><td>Channel</td><td>${Channel}</td></tr>`;
+    html += '</tbody></table>';
+    // Check if items exist
+    if (items.length === 0) {
+      html += '<h3>Items</h3><br><p>No items found in the receipt data.</p>';
+      return html;
     }
-    return number.toLocaleString(locale);
-  },
-);
 
-Helpers.registerHelpers(Handlebars);
-HandlebarsGroupBy.register(Handlebars);
+    if (items) {
+      html +=
+        '<h3>Items</h3><table id="receipt_item_table" 
class="pretty_table"><tbody>';
+      items.forEach((item: any, index: any) => {
+        let { qty } = item;
+        let rin = item.upc || item.rin || '';
+        let rsd = item.rsd || '';
+        let { amount } = item;
+        const errorMessages: string[] = [];
+
+        // Check for quantity errors in the review data
+        errors.forEach((error: any[][]) => {
+          if (error[1][1] === index && error[1][2] === 'qty') {
+            errorMessages.push('Incorrect Quantity');
+            qty += `<br><span class="is_error">${error[2][0]}</span>`; // Add 
the incorrect original quantity
+          } else if (
+            error[1][1] === index &&
+            (error[1][2] === 'rin' || error[1][2] === 'upc')
+          ) {
+            rin += `<br><span class="is_error">${error[2][0]}</span>`;
+            errorMessages.push('Incorrect RIN');
+          } else if (error[1][1] === index && error[1][2] === 'rsd') {
+            rsd += `<br><span class="is_error">${error[2][0]}</span>`;
+            errorMessages.push('Incorrect RSD');
+          } else if (error[1][1] === index && error[1][2] === 'amount') {
+            amount += `<br><span class="is_error">${error[2][0]}</span>`;
+            errorMessages.push('Incorrect amount');
+          }
+        });
+
+        const perItem = (
+          parseFloat(item.amount) / parseInt(item.qty, 10)
+        ).toFixed(2);
+
+        html += `<tr id="item_${index}" class="">
+                    <td>${item.type}</td>
+                    <td>${qty}</td>
+                    <td>${rin}</td>
+                    <td>${rsd}</td>
+                    <td>${amount}</td>
+                    <td>${perItem}</td>
+                    <td>${errorMessages.join(', ')}</td>
+                </tr>`;

Review Comment:
   ### Unescaped HTML Interpolation <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Direct interpolation of potentially untrusted data into HTML string without 
proper escaping could lead to XSS vulnerabilities.
   
   ###### Why this matters
   If any of these fields contain malicious HTML/JavaScript, it would be 
executed when rendered in the browser.
   
   ###### Suggested change ∙ *Feature Preview*
   ```typescript
   function escapeHtml(unsafe: string) {
     return unsafe
       .replace(/&/g, "&amp;")
       .replace(/</g, "&lt;")
       .replace(/>/g, "&gt;")
       .replace(/"/g, "&quot;")
       .replace(/'/g, "&#039;");
   }
   
   html += `<tr id="item_${escapeHtml(String(index))}" class="">
       <td>${escapeHtml(item.type)}</td>
       <td>${escapeHtml(String(qty))}</td>
       // ... escape other fields similarly
   </tr>`;
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/427c7f0a-3e37-4a7b-8961-a6d6db5a6726?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:0ee7bf6a-992b-44ad-a27a-df74aa61e5f9 -->
   



##########
docker/pythonpath_dev/superset_config.py:
##########
@@ -97,13 +97,32 @@ class CeleryConfig:
 
 CELERY_CONFIG = CeleryConfig
 
-FEATURE_FLAGS = {"ALERT_REPORTS": True}
+FEATURE_FLAGS = {"ALERT_REPORTS": True,"EMBEDDED_SUPERSET": 
True,"ENABLE_TEMPLATE_PROCESSING": True,}
+# EMBED CODE  IFRAME OPTIONS
+TALISMAN_ENABLED = False
+ENABLE_CORS = True
+HTTP_HEADERS={"X-Frame-Options":"ALLOWALL"} 
+
+# Dashboard embedding 
+GUEST_ROLE_NAME = "Admin" 
+GUEST_TOKEN_JWT_SECRET = 
"0f9792d37bddef9d94a1df80e3c7d04f2003b73fdb08edb2781681a73a2e13e67050d07b0c458165fcb8ea0d332ce5e3411aa4b4f5a006dbd5c5728a9a003a45209d386be26fb36f44878156f77ed14379dbc131e353cf2a8f3d876b413bf7d5d90a592b213a6f572cdd74c9b6ddb7e94a948f12be3bc97de0254eda7514e9e9a918f54e8026d4f611ea9a5a12d8a2ffeed51b80dd4030c88fdf6a96ec6860af33294e50bbc816bc8d78fadd649351f7c187637ef2b49ea5aa2c64af99bf6a24ac18518f1fe8bd7c7af00bc27e4026011e2cb2f88eea7ccc32f967708be454219f1b94d323d7664909d16369ec9edcc23afcb0c9de6a6b3168e352c5c1d590f1"
 

Review Comment:
   ### Hardcoded JWT Secret <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   JWT secret key is hardcoded in the configuration file instead of being 
sourced from environment variables.
   
   ###### Why this matters
   Hardcoded secrets in source code create security risks and make it difficult 
to maintain different environments securely.
   
   ###### Suggested change ∙ *Feature Preview*
   Use environment variable for the JWT secret:
   ```python
   GUEST_TOKEN_JWT_SECRET = os.getenv('GUEST_TOKEN_JWT_SECRET')
   if not GUEST_TOKEN_JWT_SECRET:
       raise ValueError('GUEST_TOKEN_JWT_SECRET environment variable must be 
set')
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/c1bbb809-4c1a-4286-935d-97e579566f2d?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:cc5b02e6-475c-4d40-8119-f8c93e99ce33 -->
   



##########
docker/pythonpath_dev/superset_config.py:
##########
@@ -97,13 +97,32 @@ class CeleryConfig:
 
 CELERY_CONFIG = CeleryConfig
 
-FEATURE_FLAGS = {"ALERT_REPORTS": True}
+FEATURE_FLAGS = {"ALERT_REPORTS": True,"EMBEDDED_SUPERSET": 
True,"ENABLE_TEMPLATE_PROCESSING": True,}
+# EMBED CODE  IFRAME OPTIONS
+TALISMAN_ENABLED = False
+ENABLE_CORS = True
+HTTP_HEADERS={"X-Frame-Options":"ALLOWALL"} 
+
+# Dashboard embedding 
+GUEST_ROLE_NAME = "Admin" 

Review Comment:
   ### Insecure Guest Access Configuration <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Security configuration that grants Admin role to guest users and disables 
security protections is dangerous for production environments.
   
   ###### Why this matters
   This configuration could allow unauthorized access to sensitive data and 
administrative functions, potentially leading to data breaches.
   
   ###### Suggested change ∙ *Feature Preview*
   Implement proper role-based access control for guest users and maintain 
security headers:
   ```python
   TALISMAN_ENABLED = True
   ENABLE_CORS = False  # Enable only if needed with specific origins
   HTTP_HEADERS={"X-Frame-Options":"SAMEORIGIN"}
   
   # Dashboard embedding
   GUEST_ROLE_NAME = "Viewer"  # Use a restricted role
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/1912b2d2-b7a5-4300-94ba-1ac9316ce274?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:fa2bb469-0bb4-4fe8-bf30-de4e29c5e851 -->
   



##########
superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx:
##########
@@ -76,28 +96,159 @@ export const HandlebarsViewer = ({
 };
 
 //  usage: {{dateFormat my_date format="MMMM YYYY"}}
-Handlebars.registerHelper('dateFormat', function (context, block) {
+hb.registerHelper('dateFormat', function (context: any, block: any) {
   const f = block.hash.format || 'YYYY-MM-DD';
   return moment(context).format(f);
 });
 
+hb.registerHelper('parseJSON', (jsonString: string) => {
+  if (jsonString === undefined)
+    throw Error('Please call with an object. Example: `parseJSON myObj`');
+  return JSON.parse(jsonString);
+});
+
+hb.registerHelper('inc', (value: string) => {
+  if (value === undefined)
+    throw Error('Please call with an object. Example: `inc @index`');
+  return parseInt(value, 10) + 1;
+});
+
 // usage: {{  }}
-Handlebars.registerHelper('stringify', (obj: any, obj2: any) => {
+hb.registerHelper('stringify', (obj: any, obj2: any) => {
   // calling without an argument
   if (obj2 === undefined)
     throw Error('Please call with an object. Example: `stringify myObj`');
   return isPlainObject(obj) ? JSON.stringify(obj) : String(obj);
 });
 
-Handlebars.registerHelper(
-  'formatNumber',
-  function (number: any, locale = 'en-US') {
-    if (typeof number !== 'number') {
-      return number;
+hb.registerHelper('formatNumber', function (number: any, locale = 'en-US') {
+  if (typeof number !== 'number') {
+    return number;
+  }
+  return number.toLocaleString(locale);
+});
+
+HandlebarsGroupBy.register(hb);
+
+// Example async function to fetch presigned URLs
+async function fetchPresignedUrls0(imageLinks: string[]): Promise<string[]> {
+  const response = await fetch('/api/v1/presigned_urls/', {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ image_links: imageLinks }),
+  });
+
+  if (response.ok) {
+    const data = await response.json();
+    return data.presigned_urls;
+  }
+  console.error('Error fetching presigned URLs');
+  return [];
+}
+
+// Register async helper
+hb.registerHelper('getPresignedUrls', async (imageLinks: string[]) => {
+  const urls = await fetchPresignedUrls0(imageLinks);
+  const urls1 = JSON.parse(urls as unknown as string);

Review Comment:
   ### Invalid JSON Double Parsing <sub>![category 
Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Double parsing of JSON response in getPresignedUrls helper. The response 
from fetchPresignedUrls0 is already parsed with response.json()
   
   ###### Why this matters
   This will cause a runtime error when trying to parse an already parsed JSON 
object, breaking the presigned URL functionality.
   
   ###### Suggested change ∙ *Feature Preview*
   Remove the unnecessary JSON.parse and directly use the response:
   ```typescript
   hb.registerHelper('getPresignedUrls', async (imageLinks: string[]) => {
     const urls = await fetchPresignedUrls0(imageLinks);
     const presignedUrls = urls.map(
       (item: { presigned_url: string }) => item.presigned_url
     );
     return presignedUrls;
   });
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/3a4ff6a5-6ed6-4b74-ad44-4b066484a058?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:61e8bbd4-f5ad-4025-865d-8b5002234c45 -->
   



##########
superset/views/api.py:
##########
@@ -126,6 +127,19 @@ def time_range(self, **kwargs: Any) -> FlaskResponse:
             error_msg = {"message": _("Unexpected time range: %(error)s", 
error=error)}
             return self.json_response(error_msg, 400)
 
+    @event_logger.log_this
+    @api
+    @handle_api_exception
+    @expose("/v1/presigned_urls/", methods=("POST",))
+    def get_presigned_urls(self) -> FlaskResponse:

Review Comment:
   ### Missing API Access Control <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Missing @has_access_api decorator for the presigned URLs endpoint, which is 
required for API security.
   
   ###### Why this matters
   Without access control, unauthorized users could potentially access and 
generate presigned URLs, leading to security vulnerabilities.
   
   ###### Suggested change ∙ *Feature Preview*
   Add the @has_access_api decorator:
   ```python
   @event_logger.log_this
   @api
   @handle_api_exception
   @has_access_api
   @expose("/v1/presigned_urls/", methods=("POST",))
   def get_presigned_urls(self) -> FlaskResponse:
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/3d9a8a5d-163f-49c7-88fd-42f437e8e807?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:6475c065-d950-4218-abc8-ec3f19e1887a -->
   



##########
superset-frontend/plugins/plugin-chart-handlebars/src/components/Handlebars/HandlebarsViewer.tsx:
##########
@@ -76,28 +96,159 @@ export const HandlebarsViewer = ({
 };
 
 //  usage: {{dateFormat my_date format="MMMM YYYY"}}
-Handlebars.registerHelper('dateFormat', function (context, block) {
+hb.registerHelper('dateFormat', function (context: any, block: any) {
   const f = block.hash.format || 'YYYY-MM-DD';
   return moment(context).format(f);
 });
 
+hb.registerHelper('parseJSON', (jsonString: string) => {
+  if (jsonString === undefined)
+    throw Error('Please call with an object. Example: `parseJSON myObj`');
+  return JSON.parse(jsonString);
+});
+
+hb.registerHelper('inc', (value: string) => {
+  if (value === undefined)
+    throw Error('Please call with an object. Example: `inc @index`');
+  return parseInt(value, 10) + 1;
+});
+
 // usage: {{  }}
-Handlebars.registerHelper('stringify', (obj: any, obj2: any) => {
+hb.registerHelper('stringify', (obj: any, obj2: any) => {
   // calling without an argument
   if (obj2 === undefined)
     throw Error('Please call with an object. Example: `stringify myObj`');
   return isPlainObject(obj) ? JSON.stringify(obj) : String(obj);
 });
 
-Handlebars.registerHelper(
-  'formatNumber',
-  function (number: any, locale = 'en-US') {
-    if (typeof number !== 'number') {
-      return number;
+hb.registerHelper('formatNumber', function (number: any, locale = 'en-US') {
+  if (typeof number !== 'number') {
+    return number;
+  }
+  return number.toLocaleString(locale);
+});
+
+HandlebarsGroupBy.register(hb);
+
+// Example async function to fetch presigned URLs
+async function fetchPresignedUrls0(imageLinks: string[]): Promise<string[]> {
+  const response = await fetch('/api/v1/presigned_urls/', {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ image_links: imageLinks }),
+  });
+
+  if (response.ok) {
+    const data = await response.json();
+    return data.presigned_urls;
+  }
+  console.error('Error fetching presigned URLs');
+  return [];
+}
+
+// Register async helper
+hb.registerHelper('getPresignedUrls', async (imageLinks: string[]) => {
+  const urls = await fetchPresignedUrls0(imageLinks);
+  const urls1 = JSON.parse(urls as unknown as string);

Review Comment:
   ### Unclear Variable Name and Complex Type Assertion <sub>![category 
Readability](https://img.shields.io/badge/Readability-0284c7)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Variable name 'urls1' uses a meaningless numeric suffix and the type 
assertion chain (as unknown as string) is unnecessarily complex.
   
   ###### Why this matters
   Complex type assertions and poor variable naming make the code harder to 
follow and understand the data flow.
   
   ###### Suggested change ∙ *Feature Preview*
   ```typescript
   const parsedUrls = JSON.parse(urls.toString());
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/da8bc4a3-83d9-4f34-b5d8-6a1ca5c3e593?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:a9f7fbae-2b0f-4d73-bcea-8bf157fbb025 -->
   



##########
docker/pythonpath_dev/superset_config.py:
##########
@@ -97,13 +97,32 @@ class CeleryConfig:
 
 CELERY_CONFIG = CeleryConfig
 
-FEATURE_FLAGS = {"ALERT_REPORTS": True}
+FEATURE_FLAGS = {"ALERT_REPORTS": True,"EMBEDDED_SUPERSET": 
True,"ENABLE_TEMPLATE_PROCESSING": True,}
+# EMBED CODE  IFRAME OPTIONS
+TALISMAN_ENABLED = False
+ENABLE_CORS = True
+HTTP_HEADERS={"X-Frame-Options":"ALLOWALL"} 
+
+# Dashboard embedding 
+GUEST_ROLE_NAME = "Admin" 
+GUEST_TOKEN_JWT_SECRET = 
"0f9792d37bddef9d94a1df80e3c7d04f2003b73fdb08edb2781681a73a2e13e67050d07b0c458165fcb8ea0d332ce5e3411aa4b4f5a006dbd5c5728a9a003a45209d386be26fb36f44878156f77ed14379dbc131e353cf2a8f3d876b413bf7d5d90a592b213a6f572cdd74c9b6ddb7e94a948f12be3bc97de0254eda7514e9e9a918f54e8026d4f611ea9a5a12d8a2ffeed51b80dd4030c88fdf6a96ec6860af33294e50bbc816bc8d78fadd649351f7c187637ef2b49ea5aa2c64af99bf6a24ac18518f1fe8bd7c7af00bc27e4026011e2cb2f88eea7ccc32f967708be454219f1b94d323d7664909d16369ec9edcc23afcb0c9de6a6b3168e352c5c1d590f1"
 
+GUEST_TOKEN_JWT_ALGO = "HS256" 
+GUEST_TOKEN_HEADER_NAME = "X-GuestToken" 
+GUEST_TOKEN_JWT_EXP_SECONDS = 900 # 15 minutes
+
 ALERT_REPORTS_NOTIFICATION_DRY_RUN = True
 WEBDRIVER_BASEURL = "http://superset:8088/";  # When using docker compose 
baseurl should be http://superset_app:8088/
 # The base URL for the email report hyperlinks.
 WEBDRIVER_BASEURL_USER_FRIENDLY = WEBDRIVER_BASEURL
 SQLLAB_CTAS_NO_LIMIT = True
 
+
+HTML_SANITIZATION_SCHEMA_EXTENSIONS = {
+        "attributes": {
+          "*": ["style","className","script","target"],
+        },
+        "tagNames": ["style","script"],
+      }

Review Comment:
   ### Unsafe HTML Sanitization Rules <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   HTML sanitization schema allows potentially dangerous script and style tags 
which could enable XSS attacks.
   
   ###### Why this matters
   Allowing script execution and unrestricted styling in HTML content creates a 
significant security vulnerability that could be exploited for malicious 
purposes.
   
   ###### Suggested change ∙ *Feature Preview*
   Remove dangerous tags and attributes from the sanitization schema:
   ```python
   HTML_SANITIZATION_SCHEMA_EXTENSIONS = {
           "attributes": {
             "*": ["className"],
           },
           "tagNames": [],
         }
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/b9b0ddd5-fc4d-4049-92fb-cac2a3b22d4d?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:b865514f-a0e6-4313-8128-afbf34bbac59 -->
   



##########
superset/views/api.py:
##########
@@ -126,6 +127,19 @@ def time_range(self, **kwargs: Any) -> FlaskResponse:
             error_msg = {"message": _("Unexpected time range: %(error)s", 
error=error)}
             return self.json_response(error_msg, 400)
 
+    @event_logger.log_this
+    @api
+    @handle_api_exception
+    @expose("/v1/presigned_urls/", methods=("POST",))
+    def get_presigned_urls(self) -> FlaskResponse:
+        image_links = request.json.get("image_links")
+        if not image_links:
+            return self.json_response({"error": "Image links required"}), 400
+        print('*#*#*** in get_presigned_urls')

Review Comment:
   ### Remove Debug Print Statement <sub>![category 
Logging](https://img.shields.io/badge/Logging-4f46e5)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Debug print statement left in production code in the get_presigned_urls 
endpoint.
   
   ###### Why this matters
   Debug statements in production code can expose sensitive information and 
pollute logs, making it harder to diagnose real issues.
   
   ###### Suggested change ∙ *Feature Preview*
   Remove the debug print statement:
   ```python
   @expose("/v1/presigned_urls/", methods=("POST",))
   def get_presigned_urls(self) -> FlaskResponse:
       image_links = request.json.get("image_links")
       if not image_links:
           return self.json_response({"error": "Image links required"}), 400
       presigned_urls = get_image_func(image_links)
       return self.json_response({"presigned_urls": presigned_urls}), 200
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/1bdeac3a-34e1-47e4-be8b-f5c001741cb4?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:7fe51e31-23fd-47ce-9827-a57b5cd58be9 -->
   



##########
scripts/build_docker.py:
##########
@@ -0,0 +1,294 @@
+#!/usr/bin/env python3
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import os
+import re
+import subprocess
+from textwrap import dedent
+
+import click
+
+REPO = "viveksingh27/bi-superset"
+CACHE_REPO = f"{REPO}-cache"
+BASE_PY_IMAGE = "3.10-slim-bookworm"
+
+
+def run_cmd(command: str, raise_on_failure: bool = True) -> str:
+    process = subprocess.Popen(
+        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 
text=True
+    )

Review Comment:
   ### Shell Injection Risk in Command Execution <sub>![category 
Security](https://img.shields.io/badge/Security-e11d48)</sub>
   
   <details>
     <summary>Tell me more</summary>
   
   ###### What is the issue?
   Using shell=True with unsanitized input in subprocess.Popen is vulnerable to 
shell injection attacks.
   
   ###### Why this matters
   If any part of the command string contains user input, an attacker could 
inject arbitrary shell commands.
   
   ###### Suggested change ∙ *Feature Preview*
   Use command lists instead of shell=True:
   ```python
   def run_cmd(command: list[str], raise_on_failure: bool = True) -> str:
       process = subprocess.Popen(
           command,
           stdout=subprocess.PIPE,
           stderr=subprocess.STDOUT,
           text=True
       )
   ```
   
   
   </details>
   
   <sub>
   
   [![Report a problem with this 
comment](https://img.shields.io/badge/Report%20a%20problem%20with%20this%20comment-gray.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmNWVjMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS10cmlhbmdsZS1hbGVydCI+PHBhdGggZD0ibTIxLjczIDE4LTgtMTRhMiAyIDAgMCAwLTMuNDggMGwtOCAxNEEyIDIgMCAwIDAgNCAyMWgxNmEyIDIgMCAwIDAgMS43My0zIi8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4=)](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/2b7cd568-b257-4d2f-b1e6-7c2e2b66a672?suggestedFixEnabled=true)
   
   💬 Looking for more details? Reply to this comment to chat with Korbit.
   </sub>
   
   <!--- korbi internal id:015eb96c-22a7-4c1a-ab2a-a6127f3e9d00 -->
   



-- 
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