{"record":{"id":"5a57e6045cdd6f8c","repo":"infiniflow/ragflow","slug":"artifact-symlinks-are-not-allowed-relative-path-5a57e6","errorCode":null,"errorMessage":"Artifact symlinks are not allowed: {relative_path}","messagePattern":"Artifact symlinks are not allowed: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/sandbox/providers/tenki.py","lineNumber":466,"sourceCode":"        errors = self._tenki_errors()\n        try:\n            entries = sandbox.fs.list(current_dir)\n        except errors.FileNotFoundError:\n            return\n        except FileNotFoundError:\n            return\n\n        # fs.list returns each entry's basename in `.path`, not an absolute\n        # path, so join it onto the directory being listed.\n        for entry in sorted(entries, key=lambda item: item.path):\n            name = posixpath.basename(entry.path)\n            remote_path = posixpath.join(current_dir, name)\n            relative_path = posixpath.join(relative_dir, name) if relative_dir else name\n\n            # Reject symlinks. `is_symlink` is not populated by every SDK\n            # release, so also inspect the stat mode bits as the reliable check.\n            if getattr(entry, \"is_symlink\", False) or stat.S_ISLNK(entry.mode or 0):\n                raise RuntimeError(f\"Artifact symlinks are not allowed: {relative_path}\")\n            if entry.is_dir:\n                self._collect_artifacts_recursive(sandbox, remote_path, relative_path, artifacts, depth + 1)\n                continue\n\n            if len(artifacts) >= self.max_artifacts:\n                raise RuntimeError(f\"Tenki execution produced more than {self.max_artifacts} artifacts.\")\n\n            size = int(entry.size or 0)\n            if size > self.max_artifact_bytes:\n                raise RuntimeError(f\"Artifact exceeds {self.max_artifact_bytes} bytes: {relative_path}\")\n\n            ext = os.path.splitext(name)[1].lower()\n            if ext not in ALLOWED_ARTIFACT_EXTENSIONS:\n                raise RuntimeError(f\"Unsupported artifact type: {relative_path}\")\n\n            content = sandbox.fs.read_bytes(remote_path)\n            artifacts.append(\n                {","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/agent/sandbox/providers/tenki.py#L448-L484","documentation":"Raised during artifact collection when an entry under artifacts/ is a symbolic link. Symlinks are rejected as a security measure: untrusted code could link to files outside the sandbox workspace (escape) or construct cycles; the check uses entry.is_symlink when populated plus the stat mode bits (stat.S_ISLNK) as the reliable fallback.","triggerScenarios":"Executed script runs os.symlink('/etc/passwd', 'artifacts/leak') or creates symlink cycles inside the artifacts directory; artifact collection then aborts before reading any linked target.","commonSituations":"Adversarial or prompt-injected code trying to exfiltrate sandbox-external files via artifacts; build-style scripts that legitimately create symlinks (npm link patterns) and are surprised by the rejection.","solutions":["Change the script to copy real files (shutil.copy) into artifacts/ instead of symlinking.","Treat this error in agent flows as a suspicious-code signal: reject or sanitize the generated program.","Do not attempt to whitelist specific link targets — the provider rejects all symlinks by design."],"exampleFix":"# before\n# script: os.symlink('/etc/passwd', 'artifacts/passwd')\n\n# after\n# script: shutil.copyfile('/etc/passwd', 'artifacts/passwd')  # only if that file is legitimately accessible","handlingStrategy":"try-catch","validationCode":"# in generated code, never place symlinks in artifacts:\n# assert no os.path.islink(p) for p in pathlib.Path('artifacts').rglob('*')","typeGuard":null,"tryCatchPattern":"try:\n    result = provider.execute_code(instance_id, code)\nexcept RuntimeError as exc:\n    if \"symlinks are not allowed\" in str(exc):\n        flag_untrusted_code(code)  # potential sandbox escape attempt\n    raise","preventionTips":["Generate copy-based scripts (shutil.copyfile), not symlink-based ones.","Treat symlink artifacts as a security signal from untrusted/LLM code; reject the program."],"tags":["security","artifacts","symlink","tenki"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}