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
19 changes: 13 additions & 6 deletions codewiki/src/be/agent_tools/str_replace_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ def flake8(file_path: str) -> str:
"""Run flake8 on a given file and return the output as a string"""
if Path(file_path).suffix != ".py":
return ""
cmd = "flake8 --isolated --select=F821,F822,F831,E111,E112,E113,E999,E902 {file_path}"
cmd = ["flake8", "--isolated", "--select=F821,F822,F831,E111,E112,E113,E999,E902", file_path]
# don't use capture_output because it's not compatible with python3.6
out = subprocess.run(cmd.format(file_path=file_path), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Use errors="replace" so non-UTF-8 bytes (e.g. GBK-encoded paths on Windows) don't crash decoding.
return out.stdout.decode("utf-8", errors="replace")

Expand Down Expand Up @@ -488,8 +488,8 @@ def view(self, path: Path, view_range: Optional[List[int]] = None):
return

out = subprocess.run(
rf"find {path} -maxdepth 2 -not -path '*/\.*'",
shell=True,
["find", str(path), "-maxdepth", "2", "-not", "-path", "*/\\.*"],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
Expand Down Expand Up @@ -772,9 +772,16 @@ async def str_replace_editor(

tool = EditTool(ctx.deps.registry, ctx.deps.absolute_docs_path)
if working_dir == "docs":
absolute_path = str(Path(ctx.deps.absolute_docs_path) / path)
base_dir = Path(ctx.deps.absolute_docs_path).resolve()
absolute_path = str(base_dir / path)
else:
absolute_path = str(Path(ctx.deps.absolute_repo_path) / path)
base_dir = Path(ctx.deps.absolute_repo_path).resolve()
absolute_path = str(base_dir / path)

# Guard against absolute path escape (e.g. Path("/base") / "/etc/passwd" = "/etc/passwd")
resolved = Path(absolute_path).resolve()
if not resolved.is_relative_to(base_dir):
return f"Error: Path '{path}' escapes the allowed directory. Access denied."

# validate command
if command != "view" and working_dir == "repo":
Expand Down
3 changes: 2 additions & 1 deletion codewiki/src/be/cluster_modules.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List, Dict, Any, Callable, Optional
from collections import defaultdict
import json
import logging
import traceback
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -121,7 +122,7 @@ def cluster_modules(
return {}

response_content = response.split("<GROUPED_COMPONENTS>")[1].split("</GROUPED_COMPONENTS>")[0]
module_tree = eval(response_content)
module_tree = json.loads(response_content)

if not isinstance(module_tree, dict):
logger.error(f"Invalid module tree format - expected dict, got {type(module_tree)}")
Expand Down
5 changes: 3 additions & 2 deletions codewiki/src/fe/template_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
Template utilities for FastAPI applications using Jinja2.
"""

from jinja2 import Environment, BaseLoader, select_autoescape
from jinja2 import BaseLoader, select_autoescape
from jinja2.sandbox import SandboxedEnvironment
from typing import Dict, Any


Expand All @@ -29,7 +30,7 @@ def render_template(template: str, context: Dict[str, Any]) -> str:
Rendered HTML string
"""
# Create Jinja2 environment with string template
env = Environment(
env = SandboxedEnvironment(
loader=StringTemplateLoader(template),
autoescape=select_autoescape(['html', 'xml']),
trim_blocks=True,
Expand Down
7 changes: 7 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ COPY README.md .
# Create output directories
RUN mkdir -p output/cache output/temp output/docs output/dependency_graphs

# Create non-root user and transfer /app ownership so the process can write to output
RUN useradd --system --uid 1001 --shell /sbin/nologin appuser \
&& chown -R appuser:appuser /app

# Set environment variables
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
Expand All @@ -38,5 +42,8 @@ EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/ || exit 1

# Switch to non-root user
USER appuser

# Default command
CMD ["python", "codewiki/run_web_app.py", "--host", "0.0.0.0", "--port", "8000"]
12 changes: 6 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ classifiers = [
dependencies = [
"click>=8.1.0",
"keyring>=24.0.0",
"GitPython>=3.1.40",
"GitPython==3.1.50",
"Jinja2>=3.1.6",
"tree-sitter>=0.23.2",
"tree-sitter-language-pack>=0.8.0",
Expand All @@ -40,12 +40,12 @@ dependencies = [
"tree-sitter-php>=0.23.0",
"tree-sitter-kotlin>=1.1.0",
"openai>=1.107.0",
"litellm>=1.77.0",
"litellm>=1.83.7",
"pydantic>=2.11.7",
"pydantic-settings>=2.10.1",
"pydantic-ai>=1.0.6",
"requests>=2.32.4",
"python-dotenv>=1.1.1",
"pydantic-ai>=1.56.0",
"requests>=2.33.0",
"python-dotenv>=1.2.2",
"rich>=14.1.0",
"networkx>=3.5",
"psutil>=7.0.0",
Expand All @@ -54,7 +54,7 @@ dependencies = [
"mermaid-py>=0.8.0",
"fastapi>=0.116.0",
"uvicorn>=0.35.0",
"python-multipart>=0.0.20",
"python-multipart>=0.0.27",
"colorama>=0.4.6",
"logfire>=4.1.0",
"coding-agent-wrapper>=0.1.2"
Expand Down
Loading