{"version": "2.1.0", "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "runs": [{"tool": {"driver": {"name": "Repobility", "informationUri": "https://repobility.com", "rules": [{"id": "JRN002", "name": "Browser storage is used for session token material", "shortDescription": {"text": "Browser storage is used for session token material"}, "fullDescription": {"text": "localStorage and sessionStorage are readable by injected JavaScript. For sensitive sessions, this turns XSS into account compromise."}, "properties": {"scanner": "repobility-journey-contract", "category": "auth", "severity": "medium", "confidence": 0.82, "cwe": "", "owasp": ""}}, {"id": "AUC001", "name": "[AUC001] No Repobility access matrix policy found: The repository uses web/API frameworks but does not define .repobilit", "shortDescription": {"text": "[AUC001] No Repobility access matrix policy found: The repository uses web/API frameworks but does not define .repobility/access.yml or equivalent authorization documentation."}, "fullDescription": {"text": "The repository uses web/API frameworks but does not define .repobility/access.yml or equivalent authorization documentation."}, "properties": {"scanner": "repobility-access-control", "category": "auth", "severity": "medium", "confidence": 0.92, "cwe": "CWE-285", "owasp": "WSTG-AUTHZ"}}, {"id": "DKR003", "name": "Compose service `langbot` image uses the latest tag", "shortDescription": {"text": "Compose service `langbot` image uses the latest tag"}, "fullDescription": {"text": "The latest tag is mutable and can change without a code review, producing different images from the same source."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "medium", "confidence": 0.94, "cwe": "", "owasp": ""}}, {"id": "DKR001", "name": "Docker final stage has no non-root USER", "shortDescription": {"text": "Docker final stage has no non-root USER"}, "fullDescription": {"text": "Docker images run as root unless the image or Dockerfile switches to a non-root user."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "medium", "confidence": 0.82, "cwe": "", "owasp": ""}}, {"id": "DKR014", "name": "Dockerfile copies broad context with incomplete .dockerignore", "shortDescription": {"text": "Dockerfile copies broad context with incomplete .dockerignore"}, "fullDescription": {"text": "COPY . or ADD . is safer when .dockerignore excludes secrets, git history, keys, and generated artifacts."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "medium", "confidence": 0.76, "cwe": "", "owasp": ""}}, {"id": "SEC002", "name": "[SEC002] Hardcoded API Key: Hardcoded API key found in source code.", "shortDescription": {"text": "[SEC002] Hardcoded API Key: Hardcoded API key found in source code."}, "fullDescription": {"text": "Use environment variables. Add the pattern to .gitignore."}, "properties": {"scanner": "repobility-threat-engine", "category": "credential_exposure", "severity": "medium", "confidence": 0.3, "cwe": "", "owasp": ""}}, {"id": "SEC017", "name": "[SEC017] Unbounded Input to LLM/External API: User input is passed to an LLM or external AI API (OpenAI, Anthropic, etc.", "shortDescription": {"text": "[SEC017] Unbounded Input to LLM/External API: User input is passed to an LLM or external AI API (OpenAI, Anthropic, etc.) without any visible length or size validation. This creates two risks: (1) Cost abuse \u2014 an attacker can send extremely"}, "fullDescription": {"text": "1) Enforce a maximum input length BEFORE sending to the API: e.g. `if len(text) > 4000: return error`. 2) Use token counting (tiktoken for OpenAI, anthropic's token counter) to enforce token-level limits. 3) Set max_tokens on the API call to cap response cost. 4) Add rate limiting per user/IP to prevent automated abuse. 5) Monitor API spend with alerts for unusual usage patterns."}, "properties": {"scanner": "repobility-threat-engine", "category": "llm_injection", "severity": "medium", "confidence": 0.8, "cwe": "", "owasp": ""}}, {"id": "SEC012", "name": "[SEC012] ZipSlip \u2014 Archive Path Traversal: Archive extraction without path validation allows writing files outside the t", "shortDescription": {"text": "[SEC012] ZipSlip \u2014 Archive Path Traversal: Archive extraction without path validation allows writing files outside the target directory."}, "fullDescription": {"text": "Validate extracted paths with os.path.realpath() and ensure they stay within the target directory."}, "properties": {"scanner": "repobility-threat-engine", "category": "path_traversal", "severity": "medium", "confidence": 1.0, "cwe": "", "owasp": ""}}, {"id": "SEC014", "name": "[SEC014] SSL Verification Disabled: SSL certificate verification is disabled, allowing man-in-the-middle attacks.", "shortDescription": {"text": "[SEC014] SSL Verification Disabled: SSL certificate verification is disabled, allowing man-in-the-middle attacks."}, "fullDescription": {"text": "Enable SSL verification. Use verify=True (default) for requests. Pin certificates if needed."}, "properties": {"scanner": "repobility-threat-engine", "category": "crypto", "severity": "medium", "confidence": 1.0, "cwe": "", "owasp": ""}}, {"id": "ERR001", "name": "[ERR001] Silent Exception Swallowing: Silently swallowing all exceptions hides bugs. Even in cleanup code, log at DEBUG ", "shortDescription": {"text": "[ERR001] Silent Exception Swallowing: Silently swallowing all exceptions hides bugs. Even in cleanup code, log at DEBUG level."}, "fullDescription": {"text": "Log the error: `except Exception: logger.debug('cleanup failed', exc_info=True)`. Or handle specific exception types."}, "properties": {"scanner": "repobility-threat-engine", "category": "error_handling", "severity": "medium", "confidence": 1.0, "cwe": "", "owasp": ""}}, {"id": "ERR002", "name": "[ERR002] Empty Catch Block: Empty catch blocks hide errors.", "shortDescription": {"text": "[ERR002] Empty Catch Block: Empty catch blocks hide errors."}, "fullDescription": {"text": "Log the error or rethrow it. Use console.error() at minimum."}, "properties": {"scanner": "repobility-threat-engine", "category": "error_handling", "severity": "medium", "confidence": 1.0, "cwe": "", "owasp": ""}}, {"id": "AGT007", "name": "localStorage write failures are swallowed silently", "shortDescription": {"text": "localStorage write failures are swallowed silently"}, "fullDescription": {"text": "localStorage quotas are small and writes can fail. Catching storage errors without a user-visible warning causes silent data loss when notes, images, or snapshots exceed quota."}, "properties": {"scanner": "repobility-agent-runtime", "category": "quality", "severity": "medium", "confidence": 0.8, "cwe": "", "owasp": ""}}, {"id": "AIC003", "name": "Duplicated implementation block across source files", "shortDescription": {"text": "Duplicated implementation block across source files"}, "fullDescription": {"text": "Duplicated blocks are a common artifact when generated code is pasted or recreated instead of reused. They increase maintenance cost because every future bug fix must be found in multiple locations."}, "properties": {"scanner": "repobility-ai-code-hygiene", "category": "quality", "severity": "medium", "confidence": 0.86, "cwe": "", "owasp": ""}}, {"id": "DKC010", "name": "Compose service lacks no-new-privileges hardening", "shortDescription": {"text": "Compose service lacks no-new-privileges hardening"}, "fullDescription": {"text": "no-new-privileges prevents processes from gaining additional privileges through setuid binaries or file capabilities."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "low", "confidence": 0.62, "cwe": "", "owasp": ""}}, {"id": "DKC006", "name": "Compose service does not declare a runtime user", "shortDescription": {"text": "Compose service does not declare a runtime user"}, "fullDescription": {"text": "If the image does not define USER internally, this service may run as root."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "low", "confidence": 0.56, "cwe": "", "owasp": ""}}, {"id": "DKR008", "name": ".dockerignore misses sensitive defaults", "shortDescription": {"text": ".dockerignore misses sensitive defaults"}, "fullDescription": {"text": ".dockerignore exists but does not cover common secret or VCS patterns."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "low", "confidence": 0.72, "cwe": "", "owasp": ""}}, {"id": "DKR011", "name": "Dockerfile installs recommended OS packages", "shortDescription": {"text": "Dockerfile installs recommended OS packages"}, "fullDescription": {"text": "Installing recommended packages often pulls in unnecessary runtime surface area."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "low", "confidence": 0.72, "cwe": "", "owasp": ""}}, {"id": "DKR010", "name": "Dockerfile leaves apt package indexes in the image layer", "shortDescription": {"text": "Dockerfile leaves apt package indexes in the image layer"}, "fullDescription": {"text": "Package indexes increase image size and can expose stale metadata in the final image layer."}, "properties": {"scanner": "repobility-docker", "category": "docker", "severity": "low", "confidence": 0.74, "cwe": "", "owasp": ""}}, {"id": "SEC006", "name": "[SEC006] XSS Risk: Direct HTML injection without sanitization.", "shortDescription": {"text": "[SEC006] XSS Risk: Direct HTML injection without sanitization."}, "fullDescription": {"text": "Use textContent instead of innerHTML. Sanitize with DOMPurify."}, "properties": {"scanner": "repobility-threat-engine", "category": "injection", "severity": "low", "confidence": 0.4, "cwe": "", "owasp": ""}}, {"id": "SEC020", "name": "[SEC020] Secret Printed to Logs: Debug or diagnostic code appears to print a credential-bearing value. This is a frequen", "shortDescription": {"text": "[SEC020] Secret Printed to Logs: Debug or diagnostic code appears to print a credential-bearing value. This is a frequent AI-assisted coding failure: the helper exposes the exact value needed for troubleshooting."}, "fullDescription": {"text": "Log only redacted, hashed, or last-four-style metadata. Rotate any secret that may have reached logs."}, "properties": {"scanner": "repobility-threat-engine", "category": "credential_exposure", "severity": "info", "confidence": 0.1, "cwe": "", "owasp": ""}}, {"id": "SEC007", "name": "[SEC007] Unsafe Deserialization: Unsafe deserialization can execute arbitrary code.", "shortDescription": {"text": "[SEC007] Unsafe Deserialization: Unsafe deserialization can execute arbitrary code."}, "fullDescription": {"text": "Use yaml.safe_load() instead of yaml.load(). Avoid pickle for untrusted data."}, "properties": {"scanner": "repobility-threat-engine", "category": "deserialization", "severity": "info", "confidence": 0.1, "cwe": "", "owasp": ""}}, {"id": "SEC015", "name": "[SEC015] Insecure Randomness for Security: Weak PRNG used in security-sensitive context. Output is predictable.", "shortDescription": {"text": "[SEC015] Insecure Randomness for Security: Weak PRNG used in security-sensitive context. Output is predictable."}, "fullDescription": {"text": "Use secrets module (Python) or crypto.getRandomValues() (JS) for security-sensitive randomness."}, "properties": {"scanner": "repobility-threat-engine", "category": "crypto", "severity": "info", "confidence": 0.25, "cwe": "", "owasp": ""}}]}}, "automationDetails": {"id": "repobility/379"}, "properties": {"repository": "langbot-app/LangBot", "repoUrl": "https://github.com/langbot-app/LangBot.git", "branch": "master"}, "results": [{"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12371, "scanner": "repobility-journey-contract", "fingerprint": "a74efba1596f00060e8578fa292b7eb3956bbd80c3de1d8f7fb57db0fa014c99", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|web/src/app/login/page.tsx|113|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/login/page.tsx"}, "region": {"startLine": 113}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12370, "scanner": "repobility-journey-contract", "fingerprint": "390c36a8af989750eef2e4253d2bed3c36e1dd68c7ff3233444a4c6273cf41c4", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|web/src/app/login/page.tsx|98|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/login/page.tsx"}, "region": {"startLine": 98}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12369, "scanner": "repobility-journey-contract", "fingerprint": "ad410d115a2a913f4152d6ca2a79e137088edd5247da88263de843009d640cea", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|64|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/infra/http/BaseHttpClient.ts"}, "region": {"startLine": 64}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12368, "scanner": "repobility-journey-contract", "fingerprint": "d9d1f87565393858fe640fdeb7708279b355e36ddc0fdf1f445895e5ab4805c7", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|172|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/qrcode-login/QrCodeLoginDialog.tsx"}, "region": {"startLine": 172}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12367, "scanner": "repobility-journey-contract", "fingerprint": "0ab89b686b8841071309d6070df18b233b5ee9dd658006c4eaf91d0fbec9cebe", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|149|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/qrcode-login/QrCodeLoginDialog.tsx"}, "region": {"startLine": 149}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12366, "scanner": "repobility-journey-contract", "fingerprint": "41e2ccf72662105a16b1c5425470a2bc126e39982102e3b6bf67401eca17dbbf", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|234|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/models-dialog/ModelsDialog.tsx"}, "region": {"startLine": 234}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12365, "scanner": "repobility-journey-contract", "fingerprint": "b39b198470be2ea1af888bc2f5591bb46501fff424700d9b1aedeee5b9168f9a", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|1269|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/home-sidebar/HomeSidebar.tsx"}, "region": {"startLine": 1269}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12364, "scanner": "repobility-journey-contract", "fingerprint": "a7d5bd27e088116bc06bcd3d6245b038791fade50246db5135c18a652722574f", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|1268|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/home-sidebar/HomeSidebar.tsx"}, "region": {"startLine": 1268}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12363, "scanner": "repobility-journey-contract", "fingerprint": "0534e9785474ec698becb67561b927f922d328970961c51bbc92eee43c74a365", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|146|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/dynamic-form/DynamicFormItemComponent.tsx"}, "region": {"startLine": 146}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12362, "scanner": "repobility-journey-contract", "fingerprint": "36aab0f1915e8e030cc8a87bff0d38316a5f85968982d76b216a1ba1de4c6bb9", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|66|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/account-settings-dialog/AccountSettingsDialog.tsx"}, "region": {"startLine": 66}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12361, "scanner": "repobility-journey-contract", "fingerprint": "276adae0144fbbe1c2e5ff2169f9a4ab90d226f1b5dedd0051b8351015cf114e", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|116|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/auth/space/callback/page.tsx"}, "region": {"startLine": 116}}}]}, {"ruleId": "JRN002", "level": "warning", "message": {"text": "Browser storage is used for session token material"}, "properties": {"repobilityId": 12360, "scanner": "repobility-journey-contract", "fingerprint": "086c0445fcaea3e9b512ff58f684445cbd692cef6ac30f4c683106bb83e5b725", "category": "auth", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Storage API call references token-like key or value names.", "evidence": {"rule_id": "JRN002", "scanner": "repobility-journey-contract", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html"], "correlation_key": "code|auth|token|74|jrn002"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/auth/space/callback/page.tsx"}, "region": {"startLine": 74}}}]}, {"ruleId": "AUC001", "level": "warning", "message": {"text": "[AUC001] No Repobility access matrix policy found: The repository uses web/API frameworks but does not define .repobility/access.yml or equivalent authorization documentation."}, "properties": {"repobilityId": 12359, "scanner": "repobility-access-control", "fingerprint": "f1305052c3ba1e6c1cdb5dccc19e58a8168cf78b176658f32b1fc823df3e9d10", "category": "auth", "severity": "medium", "confidence": 0.92, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Static route and framework evidence require project-owner confirmation.", "evidence": {"scanner": "repobility-access-control", "frameworks": ["Django"], "expected_files": [".repobility/access.yml", ".repobility/access.yaml", ".repobility/access.json", ".repobility/authorization.yml"], "correlation_key": "fp|f1305052c3ba1e6c1cdb5dccc19e58a8168cf78b176658f32b1fc823df3e9d10"}}}, {"ruleId": "DKR003", "level": "warning", "message": {"text": "Compose service `langbot` image uses the latest tag"}, "properties": {"repobilityId": 12356, "scanner": "repobility-docker", "fingerprint": "e9858141c2b65f101d1c216bec3bb3a6d4f9886dc7e9eff28e2db3df9fdc03c9", "category": "docker", "severity": "medium", "confidence": 0.94, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Image tag is latest.", "evidence": {"image": "rockchin/langbot:latest", "rule_id": "DKR003", "scanner": "repobility-docker", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", "https://docs.docker.com/scout/policy/", "https://github.com/hadolint/hadolint"], "correlation_key": "fp|e9858141c2b65f101d1c216bec3bb3a6d4f9886dc7e9eff28e2db3df9fdc03c9"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/docker-compose.yaml"}, "region": {"startLine": 20}}}]}, {"ruleId": "DKR003", "level": "warning", "message": {"text": "Compose service `langbot_plugin_runtime` image uses the latest tag"}, "properties": {"repobilityId": 12353, "scanner": "repobility-docker", "fingerprint": "219fb3081efb40bf6b58818be02fb321f7bd1b4411f35667e5d2b673a8054eed", "category": "docker", "severity": "medium", "confidence": 0.94, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Image tag is latest.", "evidence": {"image": "rockchin/langbot:latest", "rule_id": "DKR003", "scanner": "repobility-docker", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", "https://docs.docker.com/scout/policy/", "https://github.com/hadolint/hadolint"], "correlation_key": "fp|219fb3081efb40bf6b58818be02fb321f7bd1b4411f35667e5d2b673a8054eed"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/docker-compose.yaml"}, "region": {"startLine": 6}}}]}, {"ruleId": "DKR001", "level": "warning", "message": {"text": "Docker final stage has no non-root USER"}, "properties": {"repobilityId": 12351, "scanner": "repobility-docker", "fingerprint": "ca65742e81d61b7a273c861057311223038147a2589bd2d039ba6d5530cfda30", "category": "docker", "severity": "medium", "confidence": 0.82, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "No USER directive was found in the final runtime stage.", "evidence": {"rule_id": "DKR001", "scanner": "repobility-docker", "final_base": "python:3.12.7-slim", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", "https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html", "https://github.com/hadolint/hadolint"], "correlation_key": "fp|ca65742e81d61b7a273c861057311223038147a2589bd2d039ba6d5530cfda30"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 9}}}]}, {"ruleId": "DKR014", "level": "warning", "message": {"text": "Dockerfile copies broad context with incomplete .dockerignore"}, "properties": {"repobilityId": 12348, "scanner": "repobility-docker", "fingerprint": "0887371c73d2b6563c6630d8915c9434e54cd334b1b339086a789dc4b05da121", "category": "docker", "severity": "medium", "confidence": 0.76, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "Broad context copy found and .dockerignore misses sensitive defaults.", "evidence": {"rule_id": "DKR014", "scanner": "repobility-docker", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"], "correlation_key": "fp|0887371c73d2b6563c6630d8915c9434e54cd334b1b339086a789dc4b05da121", "missing_patterns": [".env", ".git", "id_rsa", "*.pem", "*.key"]}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 13}}}]}, {"ruleId": "SEC002", "level": "warning", "message": {"text": "[SEC002] Hardcoded API Key: Hardcoded API key found in source code."}, "properties": {"repobilityId": 12345, "scanner": "repobility-threat-engine", "fingerprint": "844154a09a962a4c0332e34c1cc838917eec1a69f053558fb0802983660c3bab", "category": "credential_exposure", "severity": "medium", "confidence": 0.3, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Low entropy value (3.5 bits) \u2014 may be placeholder or common string", "evidence": {"match": "API_KEY = '<redacted>'", "reason": "Low entropy value (3.5 bits) \u2014 may be placeholder or common string", "rule_id": "SEC002", "scanner": "repobility-threat-engine", "confidence": 0.3, "correlation_key": "secret|token|3|api_key redacted"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/api/http/controller/group.py"}, "region": {"startLine": 38}}}]}, {"ruleId": "SEC017", "level": "warning", "message": {"text": "[SEC017] Unbounded Input to LLM/External API: User input is passed to an LLM or external AI API (OpenAI, Anthropic, etc.) without any visible length or size validation. This creates two risks: (1) Cost abuse \u2014 an attacker can send extremely long inputs to burn through your API credits (a single 128K-token request to GPT-4 costs ~$4, and automated attacks can drain budgets in minutes). (2) Context stuffing \u2014 oversized inputs can push your system prompt out of the context window, effectively disab"}, "properties": {"repobilityId": 12342, "scanner": "repobility-threat-engine", "fingerprint": "358d20ba178993a967c1c8f77058d75f8c2148e63754ab6efbf91450c1699317", "category": "llm_injection", "severity": "medium", "confidence": 0.8, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "User input is passed to an AI/LLM API with no visible length check or rate limit. An attacker can send extremely long inputs to: (1) drain your API budget (128K tokens to GPT-4 \u2248 $4/request, automated = thousands of dollars), (2) push your system prompt out of the context window, disabling safety guardrails. Add input length validation before the API call.", "evidence": {"match": "client.chat.completions.create(**args, extra_body=extra_body", "reason": "User input is passed to an AI/LLM API with no visible length check or rate limit. An attacker can send extremely long inputs to: (1) drain your API budget (128K tokens to GPT-4 \u2248 $4/request, automated = thousands of dollars), (2) push your system prompt out of the context window, disabling safety guardrails. Add input length validation before the API call.", "rule_id": "SEC017", "scanner": "repobility-threat-engine", "confidence": 0.8, "correlation_key": "fp|358d20ba178993a967c1c8f77058d75f8c2148e63754ab6efbf91450c1699317"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.py"}, "region": {"startLine": 225}}}]}, {"ruleId": "SEC017", "level": "warning", "message": {"text": "[SEC017] Unbounded Input to LLM/External API: User input is passed to an LLM or external AI API (OpenAI, Anthropic, etc.) without any visible length or size validation. This creates two risks: (1) Cost abuse \u2014 an attacker can send extremely long inputs to burn through your API credits (a single 128K-token request to GPT-4 costs ~$4, and automated attacks can drain budgets in minutes). (2) Context stuffing \u2014 oversized inputs can push your system prompt out of the context window, effectively disab"}, "properties": {"repobilityId": 12341, "scanner": "repobility-threat-engine", "fingerprint": "1a74279ac8a30c450d27e3aa09069d9023740a3f4e712c7af91400a5b7ec2bd8", "category": "llm_injection", "severity": "medium", "confidence": 0.8, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "User input is passed to an AI/LLM API with no visible length check or rate limit. An attacker can send extremely long inputs to: (1) drain your API budget (128K tokens to GPT-4 \u2248 $4/request, automated = thousands of dollars), (2) push your system prompt out of the context window, disabling safety guardrails. Add input length validation before the API call.", "evidence": {"match": "client.chat.completions.create(**args, extra_body=extra_body", "reason": "User input is passed to an AI/LLM API with no visible length check or rate limit. An attacker can send extremely long inputs to: (1) drain your API budget (128K tokens to GPT-4 \u2248 $4/request, automated = thousands of dollars), (2) push your system prompt out of the context window, disabling safety guardrails. Add input length validation before the API call.", "rule_id": "SEC017", "scanner": "repobility-threat-engine", "confidence": 0.8, "correlation_key": "fp|1a74279ac8a30c450d27e3aa09069d9023740a3f4e712c7af91400a5b7ec2bd8"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py"}, "region": {"startLine": 218}}}]}, {"ruleId": "SEC012", "level": "warning", "message": {"text": "[SEC012] ZipSlip \u2014 Archive Path Traversal: Archive extraction without path validation allows writing files outside the target directory."}, "properties": {"repobilityId": 12340, "scanner": "repobility-threat-engine", "fingerprint": "2124c0249499377664bb3194f66f6c52acd24ae1fd813d2303efe3a48a1c37bb", "category": "path_traversal", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": ".extractall(", "reason": "Pattern matched with no mitigating context found", "rule_id": "SEC012", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "code|path_traversal|token|97|sec012"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/utils/version.py"}, "region": {"startLine": 97}}}]}, {"ruleId": "SEC014", "level": "warning", "message": {"text": "[SEC014] SSL Verification Disabled: SSL certificate verification is disabled, allowing man-in-the-middle attacks."}, "properties": {"repobilityId": 12339, "scanner": "repobility-threat-engine", "fingerprint": "9d510e8fbd00b468d8dc7bea3d843e994b40e402ff3a4f9be2d5727d7937cfb3", "category": "crypto", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": "CERT_NONE", "reason": "Pattern matched with no mitigating context found", "rule_id": "SEC014", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "code|crypto|token|158|sec014"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/utils/image.py"}, "region": {"startLine": 158}}}]}, {"ruleId": "ERR001", "level": "warning", "message": {"text": "[ERR001] Silent Exception Swallowing: Silently swallowing all exceptions hides bugs. Even in cleanup code, log at DEBUG level."}, "properties": {"repobilityId": 12337, "scanner": "repobility-threat-engine", "fingerprint": "c49d26db4ffbbde1aee4ba96b4c02f65bc519fe0a6eedd5d9a78196772ca19f7", "category": "error_handling", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": "except Exception:\n                            pass", "reason": "Pattern matched with no mitigating context found", "rule_id": "ERR001", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "fp|c49d26db4ffbbde1aee4ba96b4c02f65bc519fe0a6eedd5d9a78196772ca19f7"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/pipeline/monitoring_helper.py"}, "region": {"startLine": 117}}}]}, {"ruleId": "ERR001", "level": "warning", "message": {"text": "[ERR001] Silent Exception Swallowing: Silently swallowing all exceptions hides bugs. Even in cleanup code, log at DEBUG level."}, "properties": {"repobilityId": 12336, "scanner": "repobility-threat-engine", "fingerprint": "1b38f1e9bbf5c04e2d6b9dc18034e641369fbd64df4f4a54c03ea2bb7672e721", "category": "error_handling", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": "except Exception:\n                    pass", "reason": "Pattern matched with no mitigating context found", "rule_id": "ERR001", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "fp|1b38f1e9bbf5c04e2d6b9dc18034e641369fbd64df4f4a54c03ea2bb7672e721"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/pipeline/pipelinemgr.py"}, "region": {"startLine": 129}}}]}, {"ruleId": "ERR001", "level": "warning", "message": {"text": "[ERR001] Silent Exception Swallowing: Silently swallowing all exceptions hides bugs. Even in cleanup code, log at DEBUG level."}, "properties": {"repobilityId": 12335, "scanner": "repobility-threat-engine", "fingerprint": "67ffd5843a35c05a57aa23b4d1cafb5f93024720d2a3be3204ae8b6f970c2273", "category": "error_handling", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": "except Exception:\n                                pass", "reason": "Pattern matched with no mitigating context found", "rule_id": "ERR001", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "fp|67ffd5843a35c05a57aa23b4d1cafb5f93024720d2a3be3204ae8b6f970c2273"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/telemetry/telemetry.py"}, "region": {"startLine": 102}}}]}, {"ruleId": "ERR002", "level": "warning", "message": {"text": "[ERR002] Empty Catch Block: Empty catch blocks hide errors."}, "properties": {"repobilityId": 12332, "scanner": "repobility-threat-engine", "fingerprint": "9f1f119d0f5b1a9cfc4ae7e8a7f4260b096bdbeee9ffa89753fdd22ef461c15f", "category": "error_handling", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": ".catch(() => {})", "reason": "Pattern matched with no mitigating context found", "rule_id": "ERR002", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "fp|9f1f119d0f5b1a9cfc4ae7e8a7f4260b096bdbeee9ffa89753fdd22ef461c15f"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/login/page.tsx"}, "region": {"startLine": 102}}}]}, {"ruleId": "ERR002", "level": "warning", "message": {"text": "[ERR002] Empty Catch Block: Empty catch blocks hide errors."}, "properties": {"repobilityId": 12331, "scanner": "repobility-threat-engine", "fingerprint": "caa3a7836127a7f2b8dc172e43512889027a4930c88349b9572d751953489b99", "category": "error_handling", "severity": "medium", "confidence": 0.45, "triageState": "open", "verdict": "likely_fp", "isResolved": false, "reason": "Pattern matched with no mitigating context found | [R34-retro auto-suppress: setup/install wizard (placeholder values)]", "evidence": {"match": ".catch(() => {})", "reason": "Pattern matched with no mitigating context found", "rule_id": "ERR002", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "fp|caa3a7836127a7f2b8dc172e43512889027a4930c88349b9572d751953489b99"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/wizard/page.tsx"}, "region": {"startLine": 181}}}]}, {"ruleId": "ERR002", "level": "warning", "message": {"text": "[ERR002] Empty Catch Block: Empty catch blocks hide errors."}, "properties": {"repobilityId": 12330, "scanner": "repobility-threat-engine", "fingerprint": "230479fde0bc3055faf9cee6e024c08d98773c3384652f3f0008a18a58c1c2a7", "category": "error_handling", "severity": "medium", "confidence": 1.0, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "Pattern matched with no mitigating context found", "evidence": {"match": ".catch(() => {})", "reason": "Pattern matched with no mitigating context found", "rule_id": "ERR002", "scanner": "repobility-threat-engine", "confidence": 1.0, "correlation_key": "fp|230479fde0bc3055faf9cee6e024c08d98773c3384652f3f0008a18a58c1c2a7"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/register/page.tsx"}, "region": {"startLine": 68}}}]}, {"ruleId": "AGT007", "level": "warning", "message": {"text": "localStorage write failures are swallowed silently"}, "properties": {"repobilityId": 12326, "scanner": "repobility-agent-runtime", "fingerprint": "d3172cfa2a72caa704d24b6fec580494416acbd4b49e9d82dc508dc7d5887922", "category": "quality", "severity": "medium", "confidence": 0.8, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "File writes to localStorage and has an empty or ignore-only catch block without QuotaExceededError handling.", "evidence": {"rule_id": "AGT007", "scanner": "repobility-agent-runtime", "references": ["https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API"], "correlation_key": "fp|d3172cfa2a72caa704d24b6fec580494416acbd4b49e9d82dc508dc7d5887922"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/home/components/home-sidebar/HomeSidebar.tsx"}, "region": {"startLine": 193}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12325, "scanner": "repobility-ai-code-hygiene", "fingerprint": "5007d05b3e7bfef03558fceab67cc20f7fa02f0af8d9b26a3c683879fccbace8", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/official_account_api/oaevent.py", "duplicate_line": 122, "correlation_key": "fp|5007d05b3e7bfef03558fceab67cc20f7fa02f0af8d9b26a3c683879fccbace8"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_customer_service_api/wecomcsevent.py"}, "region": {"startLine": 94}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12324, "scanner": "repobility-ai-code-hygiene", "fingerprint": "1128920c3a9a8a07b217596f4a69ec48bce5e8c96637bc3af0a8c263bd426aed", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/dingtalk_api/dingtalkevent.py", "duplicate_line": 52, "correlation_key": "fp|1128920c3a9a8a07b217596f4a69ec48bce5e8c96637bc3af0a8c263bd426aed"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_customer_service_api/wecomcsevent.py"}, "region": {"startLine": 82}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12323, "scanner": "repobility-ai-code-hygiene", "fingerprint": "acc19e356d1f1fe1e43e6364c620d93f30457ee65bc66c50558d006c6a4eb2a2", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/official_account_api/api.py", "duplicate_line": 52, "correlation_key": "fp|acc19e356d1f1fe1e43e6364c620d93f30457ee65bc66c50558d006c6a4eb2a2"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_customer_service_api/api.py"}, "region": {"startLine": 174}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12322, "scanner": "repobility-ai-code-hygiene", "fingerprint": "95f6e7e0aece33e7896b2f37bc41618a2e1a44eab29b8031d45af22f357b76a6", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/wecom_api/api.py", "duplicate_line": 47, "correlation_key": "fp|95f6e7e0aece33e7896b2f37bc41618a2e1a44eab29b8031d45af22f357b76a6"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_customer_service_api/api.py"}, "region": {"startLine": 62}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12321, "scanner": "repobility-ai-code-hygiene", "fingerprint": "98d05972b3419a2c148e1f5b99cddf18776127086c1f9906ce58fb8cf26e6760", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/dingtalk_api/dingtalkevent.py", "duplicate_line": 52, "correlation_key": "fp|98d05972b3419a2c148e1f5b99cddf18776127086c1f9906ce58fb8cf26e6760"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_api/wecomevent.py"}, "region": {"startLine": 120}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12320, "scanner": "repobility-ai-code-hygiene", "fingerprint": "84ca45131967f8e8cd4f0c186a68c14559fe75c2b363c45f34709f96dc78c568", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/official_account_api/oaevent.py", "duplicate_line": 18, "correlation_key": "fp|84ca45131967f8e8cd4f0c186a68c14559fe75c2b363c45f34709f96dc78c568"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_api/wecomevent.py"}, "region": {"startLine": 18}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12319, "scanner": "repobility-ai-code-hygiene", "fingerprint": "46e011eafff02e919ac828f26161f418451b15a115a55565ab034e6a27620fbe", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/wecom_ai_bot_api/ierror.py", "duplicate_line": 1, "correlation_key": "fp|46e011eafff02e919ac828f26161f418451b15a115a55565ab034e6a27620fbe"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_api/ierror.py"}, "region": {"startLine": 1}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12318, "scanner": "repobility-ai-code-hygiene", "fingerprint": "843ededf8889f03653ae0a75355a30d075ef2238120e5c9dc43360f82da914a0", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/official_account_api/api.py", "duplicate_line": 36, "correlation_key": "fp|843ededf8889f03653ae0a75355a30d075ef2238120e5c9dc43360f82da914a0"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_api/api.py"}, "region": {"startLine": 35}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12317, "scanner": "repobility-ai-code-hygiene", "fingerprint": "68bc52eb15a2ef2fc627071be84a91ca1174476a75380056af22ebd3af44dbd1", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py", "duplicate_line": 14, "correlation_key": "fp|68bc52eb15a2ef2fc627071be84a91ca1174476a75380056af22ebd3af44dbd1"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/wecom_api/WXBizMsgCrypt3.py"}, "region": {"startLine": 14}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12316, "scanner": "repobility-ai-code-hygiene", "fingerprint": "f1f0e21dea70c81a5570662617f2a1d9ccc178ed689c162f1bd9c43aa7f2e925", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/qq_official_api/api.py", "duplicate_line": 62, "correlation_key": "fp|f1f0e21dea70c81a5570662617f2a1d9ccc178ed689c162f1bd9c43aa7f2e925"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/slack_api/api.py"}, "region": {"startLine": 24}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12315, "scanner": "repobility-ai-code-hygiene", "fingerprint": "f262ad22bbcec5778430dc264e970d85f0825e9b5aed7c8b381c97c88ee55887", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/dingtalk_api/dingtalkevent.py", "duplicate_line": 52, "correlation_key": "fp|f262ad22bbcec5778430dc264e970d85f0825e9b5aed7c8b381c97c88ee55887"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/official_account_api/oaevent.py"}, "region": {"startLine": 110}}}]}, {"ruleId": "AIC003", "level": "warning", "message": {"text": "Duplicated implementation block across source files"}, "properties": {"repobilityId": 12314, "scanner": "repobility-ai-code-hygiene", "fingerprint": "dffe71df0e609bf479de998be6d53e2eb404e73de20f36d93be0bcc9bb5bda57", "category": "quality", "severity": "medium", "confidence": 0.86, "triageState": "open", "verdict": "confirmed", "isResolved": false, "reason": "A normalized source-code window appears in two different non-test files.", "evidence": {"lines": 12, "rule_id": "AIC003", "scanner": "repobility-ai-code-hygiene", "references": ["https://jscpd.dev/"], "duplicate_file": "src/langbot/libs/coze_server_api/client.py", "duplicate_line": 49, "correlation_key": "fp|dffe71df0e609bf479de998be6d53e2eb404e73de20f36d93be0bcc9bb5bda57"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/libs/dify_service_api/v1/client.py"}, "region": {"startLine": 105}}}]}, {"ruleId": "DKC010", "level": "note", "message": {"text": "Compose service lacks no-new-privileges hardening"}, "properties": {"repobilityId": 12358, "scanner": "repobility-docker", "fingerprint": "dbbfffcb8a2f3ae6459c490d500f28bc82456960092ea73756fcab0921ad3be2", "category": "docker", "severity": "low", "confidence": 0.62, "triageState": "open", "verdict": "needs_review", "isResolved": false, "reason": "App-like service has no security_opt no-new-privileges setting.", "evidence": {"rule_id": "DKC010", "scanner": "repobility-docker", "service": "langbot", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html"], "correlation_key": "fp|dbbfffcb8a2f3ae6459c490d500f28bc82456960092ea73756fcab0921ad3be2"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/docker-compose.yaml"}, "region": {"startLine": 20}}}]}, {"ruleId": "DKC006", "level": "note", "message": {"text": "Compose service does not declare a runtime user"}, "properties": {"repobilityId": 12357, "scanner": "repobility-docker", "fingerprint": "4045ae435a6a9d76592cadae7ce0a66b5d87d6c317d7ddc51b0f6f6788cb45a3", "category": "docker", "severity": "low", "confidence": 0.56, "triageState": "open", "verdict": "needs_review", "isResolved": false, "reason": "Service has no user setting and Repobility could not prove the image runs non-root.", "evidence": {"rule_id": "DKC006", "scanner": "repobility-docker", "service": "langbot", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html"], "correlation_key": "fp|4045ae435a6a9d76592cadae7ce0a66b5d87d6c317d7ddc51b0f6f6788cb45a3"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/docker-compose.yaml"}, "region": {"startLine": 20}}}]}, {"ruleId": "DKC010", "level": "note", "message": {"text": "Compose service lacks no-new-privileges hardening"}, "properties": {"repobilityId": 12355, "scanner": "repobility-docker", "fingerprint": "e4f519951eda2b3da2493b10c9a180aac09f56dda80da12bf3705c81556b971e", "category": "docker", "severity": "low", "confidence": 0.62, "triageState": "open", "verdict": "needs_review", "isResolved": false, "reason": "App-like service has no security_opt no-new-privileges setting.", "evidence": {"rule_id": "DKC010", "scanner": "repobility-docker", "service": "langbot_plugin_runtime", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html"], "correlation_key": "fp|e4f519951eda2b3da2493b10c9a180aac09f56dda80da12bf3705c81556b971e"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/docker-compose.yaml"}, "region": {"startLine": 6}}}]}, {"ruleId": "DKC006", "level": "note", "message": {"text": "Compose service does not declare a runtime user"}, "properties": {"repobilityId": 12354, "scanner": "repobility-docker", "fingerprint": "ae60d74c8c0618bdb408edeb6b8b37dedf49b4eb2c2a96ae249cf1efb1bc1179", "category": "docker", "severity": "low", "confidence": 0.56, "triageState": "open", "verdict": "needs_review", "isResolved": false, "reason": "Service has no user setting and Repobility could not prove the image runs non-root.", "evidence": {"rule_id": "DKC006", "scanner": "repobility-docker", "service": "langbot_plugin_runtime", "references": ["https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html"], "correlation_key": "fp|ae60d74c8c0618bdb408edeb6b8b37dedf49b4eb2c2a96ae249cf1efb1bc1179"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "docker/docker-compose.yaml"}, "region": {"startLine": 6}}}]}, {"ruleId": "DKR008", "level": "note", "message": {"text": ".dockerignore misses sensitive defaults"}, "properties": {"repobilityId": 12352, "scanner": "repobility-docker", "fingerprint": "aea2ad92c68c4ee1f8432bb1ec25e7d45ac12c9e1790ac2d3fffe638b1acce12", "category": "docker", "severity": "low", "confidence": 0.72, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "A Docker build context should exclude secrets and repository metadata.", "evidence": {"rule_id": "DKR008", "scanner": "repobility-docker", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"], "correlation_key": "fp|aea2ad92c68c4ee1f8432bb1ec25e7d45ac12c9e1790ac2d3fffe638b1acce12", "missing_patterns": [".env", ".git", "id_rsa", "*.pem", "*.key"]}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".dockerignore"}, "region": {"startLine": 1}}}]}, {"ruleId": "DKR011", "level": "note", "message": {"text": "Dockerfile installs recommended OS packages"}, "properties": {"repobilityId": 12350, "scanner": "repobility-docker", "fingerprint": "6156a9e38005fa1db7bf224d6d6039922f0a7289ced82774e7446cc165b9e687", "category": "docker", "severity": "low", "confidence": 0.72, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "apt install appears without --no-install-recommends.", "evidence": {"rule_id": "DKR011", "scanner": "repobility-docker", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", "https://github.com/hadolint/hadolint"], "correlation_key": "fp|6156a9e38005fa1db7bf224d6d6039922f0a7289ced82774e7446cc165b9e687"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 17}}}]}, {"ruleId": "DKR010", "level": "note", "message": {"text": "Dockerfile leaves apt package indexes in the image layer"}, "properties": {"repobilityId": 12349, "scanner": "repobility-docker", "fingerprint": "248a11cb75008262eed62868f66760cc30be6d9c118125eccb1f7fc5cc5a5f53", "category": "docker", "severity": "low", "confidence": 0.74, "triageState": "open", "verdict": "likely", "isResolved": false, "reason": "apt update/install layer does not remove /var/lib/apt/lists.", "evidence": {"rule_id": "DKR010", "scanner": "repobility-docker", "references": ["https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"], "correlation_key": "fp|248a11cb75008262eed62868f66760cc30be6d9c118125eccb1f7fc5cc5a5f53"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 17}}}]}, {"ruleId": "SEC006", "level": "note", "message": {"text": "[SEC006] XSS Risk: Direct HTML injection without sanitization."}, "properties": {"repobilityId": 12334, "scanner": "repobility-threat-engine", "fingerprint": "241369d884d3c0c40b9472fa7b9ee543e7f351b789116fb1509fe847849fcbe0", "category": "injection", "severity": "low", "confidence": 0.4, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "No user-input source (request/query/fetch/URL) found \u2014 may be static content", "evidence": {"match": ".innerHTML = I", "reason": "No user-input source (request/query/fetch/URL) found \u2014 may be static content", "rule_id": "SEC006", "scanner": "repobility-threat-engine", "confidence": 0.4, "correlation_key": "code|injection|token|735|sec006"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/templates/embed/widget.js"}, "region": {"startLine": 735}}}]}, {"ruleId": "SEC020", "level": "none", "message": {"text": "[SEC020] Secret Printed to Logs: Debug or diagnostic code appears to print a credential-bearing value. This is a frequent AI-assisted coding failure: the helper exposes the exact value needed for troubleshooting."}, "properties": {"repobilityId": 12347, "scanner": "repobility-threat-engine", "fingerprint": "eea44da5bb83d7adedc7b5a051c557e546e14bf233cfd225996b3a55c721e60a", "category": "credential_exposure", "severity": "info", "confidence": 0.1, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Safe context pattern detected", "evidence": {"match": "logger.info(self.config['token'])", "reason": "Safe context pattern detected", "rule_id": "SEC020", "scanner": "repobility-threat-engine", "confidence": 0.1, "correlation_key": "secret|token|69|logger.info self.config token"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/platform/sources/wechatpad.py"}, "region": {"startLine": 693}}}]}, {"ruleId": "SEC020", "level": "none", "message": {"text": "[SEC020] Secret Printed to Logs: Debug or diagnostic code appears to print a credential-bearing value. This is a frequent AI-assisted coding failure: the helper exposes the exact value needed for troubleshooting."}, "properties": {"repobilityId": 12346, "scanner": "repobility-threat-engine", "fingerprint": "726fdf3de483003b21629e0da2db00211f5ef1452809a343d4dc3147ff0ef656", "category": "credential_exposure", "severity": "info", "confidence": 0.15, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Log message mentions credential-related metadata but does not print a credential-bearing value", "evidence": {"match": "logger.info('No token configured, starting QR code login...')", "reason": "Log message mentions credential-related metadata but does not print a credential-bearing value", "rule_id": "SEC020", "scanner": "repobility-threat-engine", "confidence": 0.15, "correlation_key": "secret|token|42|logger.info no token configured starting qr code login..."}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/platform/sources/openclaw_weixin.py"}, "region": {"startLine": 421}}}]}, {"ruleId": "SEC007", "level": "none", "message": {"text": "[SEC007] Unsafe Deserialization: Unsafe deserialization can execute arbitrary code."}, "properties": {"repobilityId": 12344, "scanner": "repobility-threat-engine", "fingerprint": "75a8c658f3cd3dc5fdde85e80595b258a7751e1b3e1c3fc3fbdd4d14724d8684", "category": "deserialization", "severity": "info", "confidence": 0.1, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Safe pattern 'FullLoader' detected on same line", "evidence": {"match": "yaml.load(", "reason": "Safe pattern 'FullLoader' detected on same line", "rule_id": "SEC007", "scanner": "repobility-threat-engine", "confidence": 0.1, "correlation_key": "code|deserialization|token|218|sec007"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/core/stages/load_config.py"}, "region": {"startLine": 218}}}]}, {"ruleId": "SEC007", "level": "none", "message": {"text": "[SEC007] Unsafe Deserialization: Unsafe deserialization can execute arbitrary code."}, "properties": {"repobilityId": 12343, "scanner": "repobility-threat-engine", "fingerprint": "244b3a86092aaf1c102a48e7409f12ec39b19532cb00acb6f1dff5a0cdd1c53f", "category": "deserialization", "severity": "info", "confidence": 0.1, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Safe pattern 'FullLoader' detected on same line", "evidence": {"match": "yaml.load(", "reason": "Safe pattern 'FullLoader' detected on same line", "rule_id": "SEC007", "scanner": "repobility-threat-engine", "confidence": 0.1, "correlation_key": "code|deserialization|token|50|sec007"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/config/impls/yaml.py"}, "region": {"startLine": 50}}}]}, {"ruleId": "ERR001", "level": "none", "message": {"text": "[ERR001] Silent Exception Swallowing (and 16 more): Same pattern found in 16 additional files. Review if needed."}, "properties": {"repobilityId": 12338, "scanner": "repobility-threat-engine", "fingerprint": "82892fb780fcee22a6f46c3754f57bc2b59002b85e16305a219d6b9c89f2884e", "category": "error_handling", "severity": "info", "confidence": 0.2, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Deduplicated summary only: 16 additional occurrences found. The top occurrences remain visible as actionable findings.", "evidence": {"reason": "Deduplicated summary only: 16 additional occurrences found. The top occurrences remain visible as actionable findings.", "rule_id": "ERR001", "scanner": "repobility-threat-engine", "confidence": 0.2, "correlation_key": "fp|82892fb780fcee22a6f46c3754f57bc2b59002b85e16305a219d6b9c89f2884e"}}}, {"ruleId": "ERR002", "level": "none", "message": {"text": "[ERR002] Empty Catch Block (and 6 more): Same pattern found in 6 additional files. Review if needed."}, "properties": {"repobilityId": 12333, "scanner": "repobility-threat-engine", "fingerprint": "79beb8c79c8fe2afad3d97b1aaa69b9e44070a54ac39178f92cc366b51132c53", "category": "error_handling", "severity": "info", "confidence": 0.2, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Deduplicated summary only: 6 additional occurrences found. The top occurrences remain visible as actionable findings.", "evidence": {"reason": "Deduplicated summary only: 6 additional occurrences found. The top occurrences remain visible as actionable findings.", "rule_id": "ERR002", "scanner": "repobility-threat-engine", "confidence": 0.2, "correlation_key": "fp|79beb8c79c8fe2afad3d97b1aaa69b9e44070a54ac39178f92cc366b51132c53"}}}, {"ruleId": "SEC015", "level": "none", "message": {"text": "[SEC015] Insecure Randomness for Security: Weak PRNG used in security-sensitive context. Output is predictable."}, "properties": {"repobilityId": 12329, "scanner": "repobility-threat-engine", "fingerprint": "7fc3d8122e9e3f65849a1151a9236017f0f2ba6ed709b8c8b1b0a0c5b3107d2f", "category": "crypto", "severity": "info", "confidence": 0.25, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Weak PRNG appears to be used for non-security behavior (UI, sampling, demos, shuffling, or backoff), not for secrets", "evidence": {"match": "random.random()", "reason": "Weak PRNG appears to be used for non-security behavior (UI, sampling, demos, shuffling, or backoff), not for secrets", "rule_id": "SEC015", "scanner": "repobility-threat-engine", "confidence": 0.25, "correlation_key": "code|crypto|token|21|sec015"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/langbot/pkg/pipeline/resprule/rules/random.py"}, "region": {"startLine": 21}}}]}, {"ruleId": "SEC015", "level": "none", "message": {"text": "[SEC015] Insecure Randomness for Security: Weak PRNG used in security-sensitive context. Output is predictable."}, "properties": {"repobilityId": 12328, "scanner": "repobility-threat-engine", "fingerprint": "99e69bfcb2cf91790470073a35e3e9cf7a87856a6540acb3541de7bd22f20cdd", "category": "crypto", "severity": "info", "confidence": 0.25, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Weak PRNG appears to be used for non-security behavior (UI, sampling, demos, shuffling, or backoff), not for secrets", "evidence": {"match": "Math.random()", "reason": "Weak PRNG appears to be used for non-security behavior (UI, sampling, demos, shuffling, or backoff), not for secrets", "rule_id": "SEC015", "scanner": "repobility-threat-engine", "confidence": 0.25, "correlation_key": "code|crypto|web/src/app/wizard/page.tsx|1177|sec015"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/app/wizard/page.tsx"}, "region": {"startLine": 1177}}}]}, {"ruleId": "SEC015", "level": "none", "message": {"text": "[SEC015] Insecure Randomness for Security: Weak PRNG used in security-sensitive context. Output is predictable."}, "properties": {"repobilityId": 12327, "scanner": "repobility-threat-engine", "fingerprint": "577b8b8ed3ab781edd1cdd1dfa427c1cf2365356bb18a34cb4bbe148cfa80c7b", "category": "crypto", "severity": "info", "confidence": 0.15, "triageState": "false_positive", "verdict": "likely_fp", "isResolved": true, "reason": "Weak PRNG appears to be used for non-security behavior (UI, sampling, demos, shuffling, or backoff), not for secrets", "evidence": {"match": "Math.random()", "reason": "Weak PRNG appears to be used for non-security behavior (UI, sampling, demos, shuffling, or backoff), not for secrets", "rule_id": "SEC015", "scanner": "repobility-threat-engine", "confidence": 0.15, "correlation_key": "code|crypto|token|617|sec015"}}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "web/src/components/ui/sidebar.tsx"}, "region": {"startLine": 617}}}]}]}]}