{"record":{"id":"70c362f4d5894eba","repo":"infiniflow/ragflow","slug":"artifact-symlinks-are-not-allowed-path-name","errorCode":null,"errorMessage":"Artifact symlinks are not allowed: {path.name}","messagePattern":"Artifact symlinks are not allowed: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/sandbox/providers/local.py","lineNumber":320,"sourceCode":"\n    @staticmethod\n    def _set_resource_limit(kind: int, value: int) -> None:\n        import resource\n\n        _, hard = resource.getrlimit(kind)\n        limit = value if hard == resource.RLIM_INFINITY else min(value, hard)\n        resource.setrlimit(kind, (limit, limit))\n\n    def _validate_output_size(self, stdout: str, stderr: str) -> None:\n        output_size = len((stdout or \"\").encode(\"utf-8\")) + len((stderr or \"\").encode(\"utf-8\"))\n        if output_size > self.max_output_bytes:\n            raise RuntimeError(f\"Local execution output exceeded {self.max_output_bytes} bytes.\")\n\n    def _collect_artifacts(self, artifacts_dir: Path) -> list[dict[str, Any]]:\n        artifacts: list[dict[str, Any]] = []\n        for path in sorted(artifacts_dir.rglob(\"*\")):\n            if path.is_symlink():\n                raise RuntimeError(f\"Artifact symlinks are not allowed: {path.name}\")\n            if path.is_dir():\n                continue\n            if not path.is_file():\n                raise RuntimeError(f\"Unsupported artifact entry: {path.name}\")\n\n            if len(artifacts) >= self.max_artifacts:\n                raise RuntimeError(f\"Local execution produced more than {self.max_artifacts} artifacts.\")\n\n            size = path.stat().st_size\n            if size > self.max_artifact_bytes:\n                raise RuntimeError(f\"Artifact exceeds {self.max_artifact_bytes} bytes: {path.name}\")\n\n            ext = path.suffix.lower()\n            if ext not in ALLOWED_ARTIFACT_EXTENSIONS:\n                raise RuntimeError(f\"Unsupported artifact type: {path.name}\")\n\n            artifacts.append(\n                {","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/agent/sandbox/providers/local.py#L302-L338","documentation":"Raised by LocalProvider._collect_artifacts() while scanning the instance's artifacts directory when an entry is a symlink. Symlinks are rejected outright as a sandbox-escape / size-limit evasion vector: a symlink could point outside the instance directory (reading arbitrary host files) or be used to bypass per-file size checks. The whole run fails with RuntimeError and artifacts are not returned.","triggerScenarios":"Executed code creating a symlink inside its artifacts directory, e.g. os.symlink('/etc/passwd', 'artifacts/leak.csv') or ln -s ../../big.bin artifacts/data.bin. Triggered during execute_code()'s artifact collection phase, after the process exits.","commonSituations":"LLM-generated code 'organizing' outputs with symlinks; adversarial code deliberately probing the local provider (which the class docstring warns is not a real sandbox boundary); a library that writes symlinked convenience files into the working tree.","solutions":["Change the executed code to write real files (or copy content) into artifacts/ instead of creating symlinks: shutil.copyfile instead of os.symlink.","If you control the prompt/template, instruct the code-generation step never to use os.symlink in artifact paths.","Do not treat this as a bug to bypass — it is a security guard; if you need a file from outside artifacts/, have the code read it and write a copy into artifacts/.","Run untrusted code under self_managed provider with a real container boundary instead of LocalProvider."],"exampleFix":"# before (executed code)\nos.symlink('/data/report.csv', 'artifacts/report.csv')\n\n# after (executed code)\nimport shutil\nshutil.copyfile('/data/report.csv', 'artifacts/report.csv')","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    result = provider.execute_code(instance_id, code, \"python\")\nexcept RuntimeError as e:\n    if \"symlinks are not allowed\" in str(e):\n        # code tried os.symlink in artifacts/; regenerate/fix code to copy files\n        raise SuspiciousArtifact(str(e)) from e\n    raise","preventionTips":["Never create symlinks inside the artifacts directory in executed code; copy files instead.","Treat this error as a security signal, not a bug — audit the generated code.","Run truly untrusted code under the containerized self_managed provider."],"tags":["sandbox","artifacts","security","symlink","local-provider"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}