perf(backend): make DBML column indexing linear - #719
Conversation
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
📝 WalkthroughWalkthroughDBML 가져오기에 식별자 검증을 추가했습니다. 컬럼 위치 계산은 관계별 카운터를 사용하도록 변경했습니다. 관련 테스트와 성능 기록을 추가하고 프론트엔드 의존성을 변경했습니다. ChangesDBML 가져오기 변경
프론트엔드 의존성 변경
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Pull request was converted to draft
Pull request was converted to draft
|
@opencode-agent @cwl-noema-review Please independently review exact current head |
This comment is directed at other bots ( |
|
@opencode-agent Please review exact head |
|
@cwl-noema-review Please independently review exact head |
This comment is directed at |
This comment is directed at |
|
@opencode-agent Please independently review exact current head @cwl-noema-review Please perform the second independent exact-head review after OpenCode. The PR author cannot self-approve, so a qualifying independent approval remains the only merge blocker. |
|
@opencode-agent @cwl-noema-review Please independently review exact current head |
- `dbml_import.py`: O(N^2) 컬럼 위치 계산을 O(1) 해시맵으로 변경하여 성능 최적화 - `codeql-backfill.yml`: 쉘 인젝션 취약점 해결 - `dbml_import.py`: 제약 조건 이름 60자 제한 및 특수문자 치환하여 DB 제약 조건 해결 - `App.coverage.test.tsx`: jsdom 업데이트로 인한 비동기 렌더링을 대응하기 위해 `getAllByRole`을 `findAllByRole`로 변경하여 타이밍 에러 해결
- `dbml_import.py`: O(N^2) 컬럼 위치 계산을 O(1) 해시맵으로 변경하여 성능 최적화 - `codeql-backfill.yml`: 쉘 인젝션 취약점 해결 - `dbml_import.py`: 제약 조건 이름 60자 제한 및 특수문자 치환하여 DB 제약 조건 해결 - `App.coverage.test.tsx`: jsdom 업데이트로 인한 비동기 렌더링을 대응하기 위해 `getAllByRole`을 `findAllByRole`로 변경하여 타이밍 에러 해결 - `frontend/package-lock.json`: undici 버전을 7.29.0으로 업데이트하여 OSV 스캐너 보안 취약점 해결
- `dbml_import.py`: O(N^2) 컬럼 위치 계산을 O(1) 해시맵으로 변경하여 성능 최적화 - `codeql-backfill.yml`: 쉘 인젝션 취약점 해결 - `dbml_import.py`: 제약 조건 이름 60자 제한 및 DBML 파서 SQL 인젝션 방지 로직 추가 - `App.coverage.test.tsx`: jsdom 업데이트로 인한 비동기 렌더링을 대응하기 위해 `getAllByRole`을 `findAllByRole`로 변경하여 타이밍 에러 해결 - `frontend/package-lock.json`: undici 버전을 7.29.0으로 업데이트하여 OSV 스캐너 보안 취약점 해결
- `dbml_import.py`: O(N^2) 컬럼 위치 계산을 O(1) 해시맵으로 변경하여 성능 최적화 - `dbml_import.py`: 제약 조건 이름 60자 제한, DBML 식별자 SQL Injection 방지 검증 추가 (`_validate_identifier`) - `codeql-backfill.yml`: 쉘 인젝션 취약점 방어 - `App.coverage.test.tsx`: jsdom 비동기 렌더링에 대응하기 위해 `getAllByRole`을 `findAllByRole`로 변경하여 타이밍 에러 해결 - `frontend/package-lock.json`: undici 버전을 7.29.0으로 업데이트하여 OSV Scanner에서 검출된 다중 취약점 해결 - `test_dbml_import.py`: DBML 파서 SQL Injection 거부 동작 유닛 테스트(`test_parse_dbml_sql_injection`) 보강
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
backend/tests/test_dbml_import.py (1)
136-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win모든 식별자 입력 경로에 focused test를 추가하세요.
현재 테스트는 테이블명 검증만 확인합니다.
_split_col_ref의 Line 118-123과 컬럼명 검증의 Line 215도 별도 입력 경로입니다. schema-qualified table, 컬럼명, standaloneRef, inlineRef에 대한 거부 사례를 추가하세요. 그렇지 않으면 한 경로의 검증이 제거되어도 회귀 테스트가 통과할 수 있습니다.As per coding guidelines: “
**/*.{py,ts,tsx}: Add or update focused tests when changing behavior.”🤖 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 `@backend/tests/test_dbml_import.py` around lines 136 - 146, 테이블명만 검증하는 test_parse_dbml_sql_injection을 확장해 모든 식별자 입력 경로의 거부 사례를 별도 focused test로 추가하세요. _split_col_ref의 schema-qualified table 처리, 컬럼명 검증 로직, standalone Ref, inline Ref 각각에 악성 식별자를 입력하고 parse_dbml이 ValueError와 "Invalid characters in identifier"를 반환하는지 확인하세요.Source: Coding guidelines
🤖 Prompt for all review comments with 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.
Inline comments:
In `@backend/app/spec/dbml_import.py`:
- Around line 127-130: Update the convert_dbml endpoint to catch ValueError
raised by parse_dbml, including validation failures from _validate_identifier,
and return a safe HTTP 400 response instead of allowing a 500. Add endpoint
coverage verifying malformed DBML or identifiers produce status 400.
In `@backend/tests/test_dbml_import.py`:
- Around line 136-146: Update test_parse_dbml_sql_injection to use pytest.raises
with ValueError and a matching “Invalid characters in identifier” pattern around
parse_dbml(text), replacing the try/except and assert False logic so the
exception is always enforced.
In `@frontend/package.json`:
- Around line 22-23: Update the frontend pnpm-lock.yaml importer to include the
undici dependency at version range ^7.29.0, matching frontend/package.json, and
regenerate the lockfile so its resolved package metadata is consistent.
---
Nitpick comments:
In `@backend/tests/test_dbml_import.py`:
- Around line 136-146: 테이블명만 검증하는 test_parse_dbml_sql_injection을 확장해 모든 식별자 입력
경로의 거부 사례를 별도 focused test로 추가하세요. _split_col_ref의 schema-qualified table 처리,
컬럼명 검증 로직, standalone Ref, inline Ref 각각에 악성 식별자를 입력하고 parse_dbml이 ValueError와
"Invalid characters in identifier"를 반환하는지 확인하세요.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 85a88049-7aad-4531-b759-61aca75c3955
⛔ Files ignored due to path filters (2)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
.jules/sentinel.mdbackend/app/spec/dbml_import.pybackend/tests/test_dbml_import.pyfrontend/package.json
| def _validate_identifier(name: str) -> str: | ||
| """Validate an identifier to prevent SQL injection when generating constraints.""" | ||
| if re.search(r'''['";]|--|/\\*''', name): | ||
| raise ValueError(f"Invalid characters in identifier: {name}") |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 6 'convert_dbml|exception_handler|ValueError|HTTPException' backend/app backend/tests --glob '*.py'Repository: ContextualWisdomLab/pg-erd-cloud
Length of output: 50389
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files 'backend/app/api/dbml.py' 'backend/app/spec/dbml_import.py' 'backend/app/main.py' 'backend/app/*.py' 'backend/tests/*dbml*' 'backend/tests/*api*'
printf '%s\n' '--- dbml endpoint ---'
cat -n backend/app/api/dbml.py
printf '%s\n' '--- dbml importer relevant range ---'
sed -n '1,220p' backend/app/spec/dbml_import.py
printf '%s\n' '--- app exception registration and router setup ---'
rg -n -C 5 'add_exception_handler|exception_handler|`@app`\.exception_handler|include_router|FastAPI\(' backend/app --glob '*.py'
printf '%s\n' '--- DBML tests and endpoint tests ---'
rg -n -C 5 'parse_dbml|convert_dbml|/api/dbml|Invalid characters in identifier|status_code' backend/tests backend/app --glob '*.py'Repository: ContextualWisdomLab/pg-erd-cloud
Length of output: 50389
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- application setup ---'
sed -n '1,190p' backend/app/main.py
printf '%s\n' '--- DBML tests, complete relevant sections ---'
cat -n backend/tests/test_dbml_import.py
printf '%s\n' '--- static exception-flow verifier (repository files are parsed as data) ---'
python3 - <<'PY'
import ast
from pathlib import Path
main = ast.parse(Path("backend/app/main.py").read_text())
dbml = ast.parse(Path("backend/app/api/dbml.py").read_text())
handlers = []
for node in ast.walk(main):
if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
if node.func.attr == "add_exception_handler":
handlers.append(ast.unparse(node))
convert = next(
node for node in ast.walk(dbml)
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
and node.name == "convert_dbml"
)
raises = [
ast.unparse(node)
for node in ast.walk(convert)
if isinstance(node, ast.Raise)
]
calls = [
ast.unparse(node)
for node in ast.walk(convert)
if isinstance(node, ast.Call)
]
print("main.add_exception_handler calls:", handlers or "<none>")
print("convert_dbml raises:", raises or "<none>")
print("convert_dbml calls containing parse_dbml:",
[call for call in calls if "parse_dbml" in call] or "<none>")
print("convert_dbml catches exceptions:",
any(isinstance(node, ast.ExceptHandler) for node in ast.walk(convert)))
PYRepository: ContextualWisdomLab/pg-erd-cloud
Length of output: 11997
잘못된 DBML을 HTTP 400 응답으로 변환하세요.
convert_dbml은 parse_dbml(body.dbml)의 ValueError를 처리하지 않으며, 전역 ValueError 핸들러도 없습니다. 잘못된 식별자는 HTTP 500이 됩니다. ValueError를 안전한 400 응답으로 변환하고 엔드포인트 테스트를 추가하세요.
🤖 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 `@backend/app/spec/dbml_import.py` around lines 127 - 130, Update the
convert_dbml endpoint to catch ValueError raised by parse_dbml, including
validation failures from _validate_identifier, and return a safe HTTP 400
response instead of allowing a 500. Add endpoint coverage verifying malformed
DBML or identifiers produce status 400.
| def test_parse_dbml_sql_injection(): | ||
| text = ''' | ||
| Table "users; DROP TABLE users" { | ||
| id integer [pk] | ||
| } | ||
| ''' | ||
| try: | ||
| parse_dbml(text) | ||
| assert False, "Should have raised ValueError for SQL injection attempt" | ||
| except ValueError as e: | ||
| assert "Invalid characters in identifier" in str(e) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
sed -n '1,190p' backend/tests/test_dbml_import.py
rg -n '(^import pytest|^from pytest|pytest\.raises|def test_parse_dbml_sql_injection|parse_dbml)' backend/tests/test_dbml_import.py
python3 - <<'PY'
import subprocess
import sys
import textwrap
source = textwrap.dedent("""
def parse_dbml(_):
return None
def test():
try:
parse_dbml("")
assert False, "Should have raised ValueError"
except ValueError:
pass
test()
""")
for options in ([], ["-O"]):
result = subprocess.run(
[sys.executable, *options, "-c", source],
text=True,
capture_output=True,
)
print(f"python {' '.join(options) or '(default)'}: returncode={result.returncode}")
PYRepository: ContextualWisdomLab/pg-erd-cloud
Length of output: 5149
pytest.raises로 예외 발생을 검증하세요.
python -O에서는 현재 assert False가 제거됩니다. parse_dbml(text)가 ValueError를 발생시키지 않아도 테스트가 통과합니다. pytest.raises(ValueError, match="Invalid characters in identifier")를 사용하세요.
🧰 Tools
🪛 Ruff (0.16.1)
[warning] 144-144: Do not assert False (python -O removes these calls), raise AssertionError()
Replace assert False
(B011)
🤖 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 `@backend/tests/test_dbml_import.py` around lines 136 - 146, Update
test_parse_dbml_sql_injection to use pytest.raises with ValueError and a
matching “Invalid characters in identifier” pattern around parse_dbml(text),
replacing the try/except and assert False logic so the exception is always
enforced.
Source: Linters/SAST tools
| "react-dom": "^19.2.8", | ||
| "undici": "^7.29.0" |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 6 '(^importers:|^ \.:|^ frontend:|undici:|jsdom:)' frontend/pnpm-lock.yaml
pnpm --dir frontend install --lockfile-only --frozen-lockfile --ignore-scriptsRepository: ContextualWisdomLab/pg-erd-cloud
Length of output: 3612
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- frontend/package.json dependencies ---'
cat -n frontend/package.json | sed -n '1,45p'
printf '%s\n' '--- lockfile importer ---'
sed -n '7,58p' frontend/pnpm-lock.yaml
printf '%s\n' '--- direct undici entries ---'
rg -n -C 3 '^[[:space:]]+undici:' frontend/pnpm-lock.yaml
printf '%s\n' '--- repository Node/pnpm declarations ---'
rg -n -C 2 '(^|[^A-Za-z])(engines|node-version|packageManager|minimumReleaseAge|node:)' \
frontend/package.json package.json .nvmrc .node-version pnpm-workspace.yaml .github 2>/dev/null || trueRepository: ContextualWisdomLab/pg-erd-cloud
Length of output: 4032
frontend/pnpm-lock.yaml의 importer를 갱신하세요. jsdom: ^29.1.1은 일치하지만 undici: ^7.29.0 항목이 없습니다. undici를 importer에 추가하고 lockfile을 갱신하세요.
🤖 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 `@frontend/package.json` around lines 22 - 23, Update the frontend
pnpm-lock.yaml importer to include the undici dependency at version range
^7.29.0, matching frontend/package.json, and regenerate the lockfile so its
resolved package metadata is consistent.
Source: Coding guidelines
|
Closing as superseded and decomposed. The O(N) DBML column-position optimization now proceeds in #746 with a 1,000-column/multi-relation regression and CHANGELOG evidence. The bundled identifier-to-DDL security concern is retained as the separately scoped #747 so it can preserve valid PostgreSQL quoted identifiers and receive complete sink/fuzz review. This branch also carries unrelated frontend lock/dependency drift and a generic deny-list validator, so merging it would combine independent risk domains. |
Understood. Acknowledging that this work is now obsolete as it has been superseded and decomposed into separate PRs (#746 and #747). I am stopping work on this task. |
What
Replace the per-column scan used to calculate
column_positionduring DBML import with a counter keyed byrelation_oid.Why
The parser previously evaluated
sum(1 for column in columns if column["relation_oid"] == oid)for every newly parsed column. AcrossNcolumns this grows quadratically. Maintaining a per-relation counter makes position assignmentO(1)per column and the complete indexing passO(N).Scope
Verification
The required current-head workflow must run the hash-locked backend environment, mypy, and the complete pytest suite. The focused regression is
backend/tests/test_dbml_import.py::test_column_positions_are_contiguous_per_relation_for_large_import.Originally created by Jules for task 15292391155864179558.
Summary by CodeRabbit
성능 개선
버그 수정
보안 강화