Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions WebUI/build/scripts/after-pack.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,22 @@ exports.default = async function afterPack(context) {
throw new Error(`afterPack: expected Electron binary at "${launcher}" but it was not found`)
}

fs.renameSync(launcher, realBinary)
try {
fs.renameSync(launcher, realBinary)

const wrapper = `#!/bin/bash
const wrapper = `#!/bin/bash
# Auto-generated by build/scripts/after-pack.cjs — do not edit.
# Always pass --no-sandbox: Chromium's Linux sandbox is set up before the app's
# JS runs, so the flag must be on the real command line (works on double-click).
HERE="$(dirname "$(readlink -f "$0")")"
exec "$HERE/${exe}.bin" --no-sandbox "$@"
`

fs.writeFileSync(launcher, wrapper)
fs.chmodSync(launcher, 0o755)
fs.writeFileSync(launcher, wrapper)
fs.chmodSync(launcher, 0o755)
} catch (error) {
throw new Error(`afterPack failed: ${error.message}`)
}

console.log(` • baked --no-sandbox into Linux launcher "${exe}"`)
}
12 changes: 8 additions & 4 deletions WebUI/build/scripts/ensure-electron.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function commandAvailable(cmd) {
/** A real zip starts with the local-file-header magic "PK\x03\x04". */
function looksLikeZip(file) {
try {
if (!existsSync(file) || statSync(file).size < 1024) return false
if (statSync(file).size < 1024) return false
const fd = openSync(file, 'r')
const buf = Buffer.alloc(4)
readSync(fd, buf, 0, 4, 0)
Expand All @@ -108,9 +108,13 @@ function looksLikeZip(file) {
}

// ── 1. ensure a valid cached zip ────────────────────────────────────────────
if (existsSync(cacheZip) && !looksLikeZip(cacheZip)) {
log('Cached Electron zip is invalid/partial — removing it.')
rmSync(cacheZip, { force: true })
try {
if (!looksLikeZip(cacheZip)) {
log('Cached Electron zip is invalid/partial — removing it.')
rmSync(cacheZip, { force: true })
}
} catch {
// If file doesn't exist or other error, rmSync with force: true will handle it
Comment on lines +111 to +117

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Don’t silently swallow cache invalidation failures.

This catch {} masks real filesystem errors (e.g., permissions/EBUSY), making Electron setup failures non-actionable.

Suggested patch
 try {
   if (!looksLikeZip(cacheZip)) {
     log('Cached Electron zip is invalid/partial — removing it.')
     rmSync(cacheZip, { force: true })
   }
-} catch {
-  // If file doesn't exist or other error, rmSync with force: true will handle it
+} catch (error) {
+  const detail = error instanceof Error ? error.message : String(error)
+  log(`Cache zip validation/invalidation failed: ${detail}`)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
if (!looksLikeZip(cacheZip)) {
log('Cached Electron zip is invalid/partial — removing it.')
rmSync(cacheZip, { force: true })
}
} catch {
// If file doesn't exist or other error, rmSync with force: true will handle it
try {
if (!looksLikeZip(cacheZip)) {
log('Cached Electron zip is invalid/partial — removing it.')
rmSync(cacheZip, { force: true })
}
} catch (error) {
const detail = error instanceof Error ? error.message : String(error)
log(`Cache zip validation/invalidation failed: ${detail}`)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/build/scripts/ensure-electron.mjs` around lines 111 - 117, The empty
catch block in the try-catch statement around the looksLikeZip and rmSync calls
silently swallows filesystem errors without logging them, making it difficult to
debug Electron setup failures. Instead of an empty catch block, capture the
error parameter and log it using the log function with descriptive context about
what operation failed (e.g., cache validation or removal), include the actual
error details, and consider whether setup should continue or fail based on the
error type.

}

if (!existsSync(cacheZip)) {
Expand Down
6 changes: 3 additions & 3 deletions WebUI/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import fs from 'fs'
import { exec } from 'node:child_process'
import { exec, spawn } from 'node:child_process'
import { promisify } from 'node:util'

const execAsync = promisify(exec)
Expand Down Expand Up @@ -2363,7 +2363,7 @@ function initEventHandle() {
ipcMain.on('mcp:openConfigInFolder', () => {
const configPath = getMcpConfigPath()
if (process.platform === 'win32') {
exec(`explorer.exe /select, "${configPath}"`)
spawn('explorer.exe', [`/select,${configPath}`], { detached: true, shell: false })
} else {
shell.showItemInFolder(configPath)
}
Expand Down Expand Up @@ -2444,7 +2444,7 @@ function initEventHandle() {

// Open the image with the default system image viewer
if (process.platform === 'win32') {
exec(`explorer.exe /select, "${imagePath}"`)
spawn('explorer.exe', [`/select,${imagePath}`], { detached: true, shell: false })
} else {
shell.showItemInFolder(imagePath)
}
Expand Down
9 changes: 5 additions & 4 deletions WebUI/electron/subprocesses/comfyuiTools.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { exec } from 'node:child_process'
import { exec, execFile } from 'node:child_process'
import { promisify } from 'node:util'
import path from 'node:path'
import fs from 'fs'
Expand All @@ -12,6 +12,7 @@ import {
} from './uvBasedBackends/uv'

const execAsync = promisify(exec)
const execFileAsync = promisify(execFile)

// Backend name for ComfyUI
const COMFYUI_BACKEND = 'ComfyUI'
Expand Down Expand Up @@ -97,7 +98,7 @@ async function installGitRepo(gitRepoUrl: string, targetDir: string): Promise<vo
const absoluteTargetDir = path.resolve(targetDir)
removeExistingResource(absoluteTargetDir)
const gitPath = getGitBinaryPath()
await execAsync(`"${gitPath}" clone ${gitRepoUrl} "${absoluteTargetDir}"`)
await execFileAsync(gitPath, ['clone', gitRepoUrl, absoluteTargetDir])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect Git subprocess sinks and nearby validation.
rg -n -C4 "execFileAsync\\(gitPath, \\['(clone|checkout|rev-parse)'" WebUI/electron/subprocesses/comfyuiTools.ts

Repository: TNG/AI-Playground

Length of output: 1471


🏁 Script executed:

cat -n WebUI/electron/subprocesses/comfyuiTools.ts

Repository: TNG/AI-Playground

Length of output: 19164


Block Git option injection at the argument boundary.

execFile removes shell injection, but Git still treats leading-dash values as options. Terminate clone options with -- and reject leading-dash refs before checkout.

Proposed fix
 async function installGitRepo(gitRepoUrl: string, targetDir: string): Promise<void> {
   try {
     // Ensure targetDir is an absolute path
     const absoluteTargetDir = path.resolve(targetDir)
     removeExistingResource(absoluteTargetDir)
     const gitPath = getGitBinaryPath()
-    await execFileAsync(gitPath, ['clone', gitRepoUrl, absoluteTargetDir])
+    const trimmedGitRepoUrl = gitRepoUrl.trim()
+    if (trimmedGitRepoUrl.startsWith('-')) {
+      throw new Error('Invalid git repository URL')
+    }
+    await execFileAsync(gitPath, ['clone', '--', trimmedGitRepoUrl, absoluteTargetDir])
     appLoggerInstance.info(`Cloned ${gitRepoUrl} into ${absoluteTargetDir}`, 'comfyui-tools')
   } catch (error) {
 async function checkoutGitRef(repoDir: string, gitRef?: string): Promise<void> {
   // Ensure repoDir is an absolute path
   const absoluteRepoDir = path.resolve(repoDir)
-  if (!gitRef || !gitRef.trim()) {
+  const trimmedGitRef = gitRef?.trim()
+  if (!trimmedGitRef) {
     appLoggerInstance.info(`No valid git ref provided for ${absoluteRepoDir}`, 'comfyui-tools')
     const currentRef = await getGitRef(absoluteRepoDir)
     appLoggerInstance.warn(`Repo ${absoluteRepoDir} remains in ref ${currentRef}`, 'comfyui-tools')
     return
   }
+  if (trimmedGitRef.startsWith('-')) {
+    appLoggerInstance.warn(`Invalid git ref provided for ${absoluteRepoDir}`, 'comfyui-tools')
+    return
+  }
 
   try {
     const gitPath = getGitBinaryPath()
-    await execFileAsync(gitPath, ['checkout', gitRef], { cwd: absoluteRepoDir })
-    appLoggerInstance.info(`Checked out ${gitRef} in ${absoluteRepoDir}`, 'comfyui-tools')
+    await execFileAsync(gitPath, ['checkout', trimmedGitRef], { cwd: absoluteRepoDir })
+    appLoggerInstance.info(`Checked out ${trimmedGitRef} in ${absoluteRepoDir}`, 'comfyui-tools')

Also applies to: 129-129

🧰 Tools
🪛 ast-grep (0.44.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { exec, execFile } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/electron/subprocesses/comfyuiTools.ts` at line 101, The Git clone and
checkout commands are vulnerable to option injection because Git interprets
arguments starting with dashes as options rather than values. In the
execFileAsync call for the clone command at line 101, add the `--` terminator
after the clone subcommand to stop Git from treating subsequent arguments as
options. Similarly, apply the same fix to the checkout command at line 129.
Additionally, before the checkout operation, validate and reject any git
references (refs) that start with a leading dash to prevent them from being
interpreted as Git options, ensuring only safe ref names are passed to the git
checkout command.

Source: Linters/SAST tools

appLoggerInstance.info(`Cloned ${gitRepoUrl} into ${absoluteTargetDir}`, 'comfyui-tools')
} catch (error) {
appLoggerInstance.error(
Expand Down Expand Up @@ -125,7 +126,7 @@ async function checkoutGitRef(repoDir: string, gitRef?: string): Promise<void> {

try {
const gitPath = getGitBinaryPath()
await execAsync(`"${gitPath}" checkout ${gitRef}`, { cwd: absoluteRepoDir })
await execFileAsync(gitPath, ['checkout', gitRef], { cwd: absoluteRepoDir })
appLoggerInstance.info(`Checked out ${gitRef} in ${absoluteRepoDir}`, 'comfyui-tools')
} catch (error) {
appLoggerInstance.warn(
Expand All @@ -145,7 +146,7 @@ export async function getGitRef(repoDir: string): Promise<string | undefined> {
// Ensure repoDir is an absolute path
const absoluteRepoDir = path.resolve(repoDir)
const gitPath = getGitBinaryPath()
const { stdout } = await execAsync(`"${gitPath}" rev-parse HEAD`, { cwd: absoluteRepoDir })
const { stdout } = await execFileAsync(gitPath, ['rev-parse', 'HEAD'], { cwd: absoluteRepoDir })
return stdout.trim()
} catch (error) {
const absoluteRepoDir = path.resolve(repoDir)
Expand Down
4 changes: 2 additions & 2 deletions WebUI/electron/subprocesses/langchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ async function embedInputUsingRag(embedInquiry: EmbedInquiry): Promise<Document[
const cacheBackedEmbeddings = CacheBackedEmbeddings.fromBytesStore(
underlyingEmbeddings,
documentEmbeddingStore,
{ namespace: createHash('md5').update(underlyingEmbeddings.model).digest('hex') },
{ namespace: createHash('sha256').update(underlyingEmbeddings.model).digest('hex') },
)

const vectorStore = await MemoryVectorStore.fromDocuments(
Expand All @@ -142,7 +142,7 @@ async function embedInputUsingRag(embedInquiry: EmbedInquiry): Promise<Document[
async function generateFileMD5Hash(filePath: string): Promise<string> {
try {
const fileBuffer = await readFile(filePath)
const hashSum = createHash('md5')
const hashSum = createHash('sha256')
hashSum.update(fileBuffer)
const hex = hashSum.digest('hex')
return hex
Expand Down
4 changes: 2 additions & 2 deletions WebUI/src/assets/js/store/comfyUiPresets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ export const useComfyUiPresets = defineStore(
repoInfoWithPotentialGitRefSplitted.length < 1
) {
console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`)
throw new Error('Could not extract comfyUI node description from ${workflowNodeInfoString}')
throw new Error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`)
}
const [repoInfoString, gitRef] = repoInfoWithPotentialGitRefSplitted
if (!gitRef) {
Expand All @@ -537,7 +537,7 @@ export const useComfyUiPresets = defineStore(
const repoInfoSplitted = repoInfoString.replace(' ', '').split('/')
if (repoInfoSplitted.length !== 2) {
console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`)
throw new Error('Could not extract comfyUI node description from ${workflowNodeInfoString}')
throw new Error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`)
}
const [username, repoName] = repoInfoSplitted
return { username: username, repoName: repoName, gitRef: gitRef }
Expand Down
6 changes: 3 additions & 3 deletions WebUI/src/components/AddLLMDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class="py-10 px-20 w-500px flex flex-col items-center justify-center bg-card shadow-2xl rounded-3xl gap-6 text-foreground"
:class="{ 'animate-scale-in': animate }"
>
<b v-html="i18nState.REQUEST_LLM_MODEL_NAME"></b>
<b>{{ i18nState.REQUEST_LLM_MODEL_NAME }}</b>
<div
class="flex flex-col items-center gap-2 p-4 border border-yellow-600 bg-yellow-600/10 rounded-lg"
>
Expand All @@ -31,7 +31,7 @@
v-if="showInfo"
class="absolute bg-background shadow-lg border border-border rounded-lg p-2.5 z-10 w-0.6"
>
<p v-html="i18nState.REQUEST_LLM_MODEL_DESCRIPTION"></p>
<p>{{ i18nState.REQUEST_LLM_MODEL_DESCRIPTION }}</p>
<ul>
<li>{{ exampleModelName }}</li>
</ul>
Expand Down Expand Up @@ -103,7 +103,7 @@
v-if="showVisionInfo"
class="absolute bg-background shadow-lg border border-border rounded-lg p-2.5 z-10 w-0.6"
>
<p v-html="i18nState.REQUEST_LLM_VISION_MODEL_DESCRIPTION"></p>
<p>{{ i18nState.REQUEST_LLM_VISION_MODEL_DESCRIPTION }}</p>
</span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion WebUI/src/components/LoadingBar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="v-loading">
<p class="v-loading-text" v-html="util.html2Escape(porps.text)"></p>
<p class="v-loading-text">{{ util.html2Escape(porps.text) }}</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stop escaping before Vue interpolation.

{{ ... }} already text-escapes output, so util.html2Escape(porps.text) will render characters like < as visible entities instead of the original text.

Proposed fix
-    <p class="v-loading-text">{{ util.html2Escape(porps.text) }}</p>
+    <p class="v-loading-text">{{ porps.text }}</p>

If util is no longer used in this component:

-import * as util from '`@/assets/js/util`'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p class="v-loading-text">{{ util.html2Escape(porps.text) }}</p>
<p class="v-loading-text">{{ porps.text }}</p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/src/components/LoadingBar.vue` at line 3, The issue is that
util.html2Escape() is being called on porps.text before Vue interpolation, which
causes double escaping since Vue's {{ ... }} syntax already handles HTML
escaping automatically. This will result in HTML entities being displayed as
visible text (like &lt; instead of <). Remove the util.html2Escape() function
wrapper and update the interpolation in the LoadingBar component to use {{
porps.text }} directly without any additional escaping.

<div class="v-loading-bar">
<div class="v-loading-bar-thumb"></div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion WebUI/src/components/MarkdownRenderer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { base64ToString } from 'uint8array-extras'
import { parse } from '@/assets/js/markdownParser'
import { sanitizeMarkdown } from '@/lib/sanitize'
import DOMPurify from 'dompurify'

const props = defineProps<{
content: string
Expand Down Expand Up @@ -33,7 +34,7 @@ async function updateRenderedContent(content: string) {
const nextContent = pendingContent
pendingContent = null

renderedHtml.value = sanitizeMarkdown(parse(nextContent) as string)
renderedHtml.value = DOMPurify.sanitize(sanitizeMarkdown(parse(nextContent) as string))
nextTick(attachCopyHandlers)

if (pendingContent !== null) {
Expand Down
4 changes: 1 addition & 3 deletions WebUI/src/components/ProgressBar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="v-progress-bar">
<div v-if="props.text" class="text-foreground text-center" v-html="html"></div>
<div v-if="props.text" class="text-foreground text-center whitespace-pre-wrap">{{ props.text }}</div>
<div class="v-progress-bar-percent-bg">
<div class="v-progress-bar-percent" :style="{ width: `${props.percent}%` }"></div>
</div>
Expand All @@ -11,6 +11,4 @@ const props = defineProps<{
percent: number
text?: string
}>()

const html = computed(() => (props.text == null ? '' : props.text.replace('\r\n', '<br/>')))
</script>
2 changes: 1 addition & 1 deletion WebUI/src/components/WarningDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class="py-10 px-20 w-500px flex flex-col items-center justify-center bg-card rounded-3xl gap-6 text-foreground"
:class="{ 'animate-scale-in': animate }"
>
<p v-html="warningMessage"></p>
<p>{{ warningMessage }}</p>
<div v-if="warningDontShowAgainKey" class="flex items-center gap-2 self-start">
<input
id="warning-dont-show-again"
Expand Down
4 changes: 2 additions & 2 deletions comfyui-deps/custom_nodes/OpenVINOImageUpscale/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def _candidate_model_roots() -> list[Path]:
for root in folder_paths.get_folder_paths("upscale_models"):
roots.append(Path(root))
except Exception:
pass
pass # No upscale models directory found - optional

here = Path(__file__).resolve()
for parent in [here.parent, *here.parents]:
Expand Down Expand Up @@ -148,7 +148,7 @@ def _resolve_model_file(model_ref: str) -> Path:
if resolved_path.is_file():
return resolved_path
except Exception:
pass
pass # Path resolution failed - optional fallback

# Some shipped paths use `repo---name/file` (Comfy-Org repackaged style)
# but the on-disk layout flips the first two segments to a `repo/name`
Expand Down
10 changes: 6 additions & 4 deletions comfyui-deps/custom_nodes/aipg-auth/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ async def aipg_auth_middleware(request: web.Request, handler):
if not _is_loopback_host(host_header):
_LOG.warning(
"rejecting request with non-loopback Host header %r to %s",
host_header,
request.path,
host_header.replace('\n', ' ').replace('\r', ' '),
request.path.replace('\n', ' ').replace('\r', ' '),
)
return web.json_response({"error": "loopback only"}, status=403)

Expand All @@ -177,8 +177,8 @@ async def aipg_auth_middleware(request: web.Request, handler):
if not _TOKEN:
_LOG.warning(
"AIPG_LOOPBACK_TOKEN is not set; rejecting %s %s",
request.method,
request.path,
request.method.replace('\n', ' ').replace('\r', ' '),
request.path.replace('\n', ' ').replace('\r', ' '),
)
response = web.json_response(
{"error": "ComfyUI must be launched via AI Playground"}, status=503
Expand Down Expand Up @@ -273,8 +273,10 @@ async def _aipg_launch(request: web.Request):
_COOKIE_NAME,
session_id,
httponly=True,
secure=False, # False: loopback uses http, not https
samesite="Strict",
path="/",
max_age=86400, # 24 hours
)
return response

Expand Down
7 changes: 6 additions & 1 deletion service/model_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,12 @@ def download(

def build_queue(self, file_list: list[HFFileItem]):
for file in file_list:
save_filename = path.abspath(path.join(self.save_path_tmp, file.relpath))
# Prevent path traversal: verify resolved path stays within save_path_tmp
safe_relpath = path.normpath(path.basename(file.relpath) if path.dirname(file.relpath) == '' else file.relpath)
save_filename = path.abspath(path.join(self.save_path_tmp, safe_relpath))
if not save_filename.startswith(path.abspath(self.save_path_tmp) + os.sep):
logging.warning(f'Skipping file with path traversal attempt: {file.relpath}')
continue
if path.exists(save_filename):
local_file_size = path.getsize(save_filename)
self.download_size += local_file_size
Expand Down
6 changes: 3 additions & 3 deletions service/static/assets/history.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
document.addEventListener("DOMContentLoaded", function () {
html = "";
let html = "";
for (let i = 0; i < history.length; i++) {
let item = history[i];
html += `<li><div class="result-img"><img src="${item.out_image}" /></div><ul class="params">`;
for (let j = 0; j < item.params.length; j++) {
let param = item.params[j];
if (param.type == "image") {
pos = param.value.lastIndexOf("/");
filename = pos > -1 ? param.value.substring(pos + 1) : param.value;
let pos = param.value.lastIndexOf("/");
let filename = pos > -1 ? param.value.substring(pos + 1) : param.value;
html += `<li><span class="param-name">${param.name}</span><span class="param-value"><a href='${param.value}' target='_blank'>${filename}</a></span></li>`;
} else {
html += `<li><span class="param-name">${param.name}</span><span class="param-value">${param.value}</span></li>`;
Expand Down
7 changes: 5 additions & 2 deletions service/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ def get_comfyui_faceswap_facerestore_path(type: str, repo_id: str) -> str:
# Model path and existence checking
def get_model_path(type: str, backend: str):
"""Get the model path for a given type and backend"""
logging.info(f'getting model path for type {type} and backend {backend}')
logging.info('getting model path for type %s and backend %s',
type.replace('\n', ' ').replace('\r', ' '),
backend.replace('\n', ' ').replace('\r', ' '))
match backend:
case "default":
# Default backend (old ipexllm) is no longer supported
logging.warning(f'Default backend (ipexllm) is no longer supported. Cannot get model path for type {type}.')
logging.warning('Default backend (ipexllm) is no longer supported. Cannot get model path for type %s.',
type.replace('\n', ' ').replace('\r', ' '))
return None
case "llama_cpp":
return config.llama_cpp_model_paths.get(type)
Expand Down
8 changes: 5 additions & 3 deletions service/web_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
try:
sys.addaudithook(auditor)
except Exception:
pass
logging.warning('Failed to install audit hook - optional feature')

Check failure on line 16 in service/web_api.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F821)

service/web_api.py:16:5: F821 Undefined name `logging`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Missing logging module import causes runtime NameError.

Lines 16 and 50 reference logging.warning() but the logging module is not imported. This will cause a NameError at runtime when the audit hook fails to install or when torchvision is unavailable. Ruff correctly flags this as F821 (undefined name).

Add import logging at the top of the file to resolve this critical issue.

Also applies to: 50-50

🧰 Tools
🪛 GitHub Actions: Ruff / 0_ruff.txt

[error] 16-16: Ruff: Undefined name logging (F821).

🪛 GitHub Actions: Ruff / ruff

[error] 16-16: Ruff (rule F821): Undefined name logging.

🪛 GitHub Check: ruff

[failure] 16-16: ruff (F821)
service/web_api.py:16:5: F821 Undefined name logging

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/web_api.py` at line 16, The logging module is referenced in the
logging.warning() calls at lines 16 and 50 but is not imported at the top of the
file, which will cause a NameError at runtime. Add `import logging` at the
beginning of the service/web_api.py file, before any code that uses
logging.warning(), to resolve the undefined name error.

Source: Linters/SAST tools



def get_added_dll_directories():
Expand Down Expand Up @@ -47,7 +47,7 @@

sys.modules["torchvision.transforms.functional_tensor"] = functional
except ImportError:
pass
logging.warning('torchvision not available - optional feature')

Check failure on line 50 in service/web_api.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F821)

service/web_api.py:50:13: F821 Undefined name `logging`

import hmac
import os
Expand Down Expand Up @@ -121,7 +121,9 @@
def _enforce_loopback_and_auth():
if request.remote_addr not in _LOOPBACK_REMOTE_ADDRS:
logging.warning(
f"rejecting non-loopback request from {request.remote_addr} to {request.path}"
"rejecting non-loopback request from %s to %s",
str(request.remote_addr).replace('\n', ' ').replace('\r', ' '),
str(request.path).replace('\n', ' ').replace('\r', ' ')
)
return jsonify({"error": "loopback only"}), 403
# CORS preflight requests do NOT carry the X-AIPG-Auth header by
Expand Down
Loading