From 3c1c7aae7b72e34ca9b48b578b0573736daba8fd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 00:33:03 +0900 Subject: [PATCH 1/5] perf(frontend): preserve search identity and isolate snapshot polling (#765) * test(frontend): specify search identity and sequential polling * perf(frontend): isolate search decoration and polling continuations * docs(changelog): record search and polling isolation * docs(doctoring): record search and polling evidence --- CHANGELOG.md | 1 + .../search-identity-and-sequential-polling.md | 39 +++ frontend/src/App.searchPolling.test.tsx | 290 ++++++++++++++++++ frontend/src/App.tsx | 74 +++-- 4 files changed, 384 insertions(+), 20 deletions(-) create mode 100644 docs/doctoring/search-identity-and-sequential-polling.md create mode 100644 frontend/src/App.searchPolling.test.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 679a62024..3c999061c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unreleased +- [FE] ⚡ **검색 노드 참조 안정화 및 순차 스냅샷 폴링**: 같은 정규화 검색어와 원본 테이블 데이터에는 장식된 `node.data` 참조를 재사용하여 드래그 중 불필요한 하위 렌더링과 할당을 줄입니다. 스냅샷 폴링은 이전 요청이 끝난 뒤에만 다음 요청을 예약하며, 선택 변경·언마운트 후 도착한 오래된 성공 또는 실패 응답을 무시합니다. - [BE] 🔒 **공유 export 전 경로 redaction**: 공개 share의 SQL / index-design / reversing-spec export에서 코멘트·`example_value`를 제거합니다. 단위 테스트로 누출을 차단합니다. - [BE] 🛠️ **함수 인덱스 중복 오탐 수정**: `lower(email)` 등 expression index를 평문 컬럼 인덱스의 중복으로 잘못 판단하지 않도록 괄호 파서를 강화했습니다. - [Docs] README를 상용 기준 기능 설명으로 갱신 (MVP skeleton 표현 제거, share redaction·diff/export 반영). diff --git a/docs/doctoring/search-identity-and-sequential-polling.md b/docs/doctoring/search-identity-and-sequential-polling.md new file mode 100644 index 000000000..0e0ee1900 --- /dev/null +++ b/docs/doctoring/search-identity-and-sequential-polling.md @@ -0,0 +1,39 @@ +# Search identity and sequential snapshot polling + +## Decision + +The ERD canvas treats the immutable `TableNodeData` object as the identity of the source table payload. While a normalized search query is active, a query-scoped `WeakMap` stores the derived highlight/dim payload. Re-rendering with the same source object reuses the exact derived reference; changing the normalized query replaces the cache, and replacing the source payload produces a new derived object. + +Snapshot status polling is one sequential asynchronous process per active `(selectedProjectId, snapshotId)` effect. The first request starts immediately. A non-terminal response schedules one `setTimeout` only after the request completes. Terminal states stop polling and may refresh the snapshot list. Cleanup marks the process obsolete and clears the pending timeout, so late success, refresh, or rejection continuations cannot update the current view. + +## Why + +React Flow can emit frequent position-only node updates. Reallocating every derived `node.data` object during those updates defeats reference-sensitive memoization below the canvas and creates garbage unrelated to actual table-content changes. `WeakMap` keys use object identity and do not keep otherwise unreachable key objects alive, which fits a cache whose lifetime follows the source payload. + +A fixed `setInterval` can start another request while the prior request is unresolved. Network responses are not guaranteed to complete in issue order, so an older non-terminal result can overwrite a newer terminal result. React's effect guidance explicitly recommends cleanup-scoped invalidation for manually fetched data because responses may arrive out of order. Completion-scheduled `setTimeout` polling additionally guarantees at most one in-flight status request per effect generation. + +## Invariants + +- The source `node.data` object is never mutated with search-only state. +- Equivalent normalized queries and position-only updates reuse the derived data reference. +- A changed query or changed source data object yields a fresh derived reference. +- At most one `getSnapshot` call is in flight for one effect generation. +- `succeeded`, `failed`, and `not_found` stop future status requests. +- Dependency change or unmount invalidates all pending continuations before they can publish snapshot, list, or error state. +- Polling errors remain visible only for the still-current snapshot process. + +## Verification + +`frontend/src/App.searchPolling.test.tsx` observes the `ReactFlow` node payload rather than implementation internals. It asserts reference identity with `toBe`, drives a position-only update and a source-data replacement, exercises reversed response order, rejects a superseded request with sensitive detail, and uses controlled timers to prove non-overlap and terminal shutdown. Repository CI remains authoritative for npm-only type checking, complete statement/branch/function/line coverage, and the production build. + +## Operational monitoring and rollback + +Monitor snapshot-status request concurrency, terminal-to-render latency, stale-response suppression, browser memory growth during long search/drag sessions, and frontend error rates. Roll back by restoring the prior uncached derivation and interval loop only if a verified regression requires it; doing so reopens the documented allocation and race risks and therefore requires a replacement isolation design and regression evidence. + +## References + +MDN Web Docs contributors. (2026). *WeakMap*. Mozilla. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap + +React Team. (n.d.). *useEffect*. React. Retrieved August 7, 2026, from https://react.dev/reference/react/useEffect + +Web Hypertext Application Technology Working Group. (2026, July 13). *HTML Standard: Timers*. https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers diff --git a/frontend/src/App.searchPolling.test.tsx b/frontend/src/App.searchPolling.test.tsx new file mode 100644 index 000000000..6c48313fc --- /dev/null +++ b/frontend/src/App.searchPolling.test.tsx @@ -0,0 +1,290 @@ +import '@testing-library/jest-dom/vitest' +import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const api = vi.hoisted(() => ({ + getMe: vi.fn(), + listProjects: vi.fn(), + listConnections: vi.fn(), + listSnapshots: vi.fn(), + createProject: vi.fn(), + createConnection: vi.fn(), + createSnapshot: vi.fn(), + getSnapshot: vi.fn(), + createShareLink: vi.fn(), +})) + +type CapturedNode = { + id: string + position: { x: number; y: number } + data: Record +} + +const flowCapture = vi.hoisted(() => ({ + renders: [] as CapturedNode[][], + setNodes: undefined as + | ((update: CapturedNode[] | ((current: CapturedNode[]) => CapturedNode[])) => void) + | undefined, +})) + +vi.mock('./api', () => api) +vi.mock('./erd/TableNode', () => ({ default: () => null })) +vi.mock('./erd/export', () => ({ + downloadText: vi.fn(), + exportDDL: vi.fn(() => ''), + exportDiagramSvg: vi.fn(() => ''), + exportDictionaryCsv: vi.fn(() => ''), + exportDictionaryMarkdown: vi.fn(() => ''), + exportPlantUml: vi.fn(() => ''), +})) +vi.mock('./erd/mermaid', () => ({ exportMermaid: vi.fn(() => '') })) +vi.mock('./erd/dbml', () => ({ exportDbml: vi.fn(() => '') })) +vi.mock('./erd/prisma', () => ({ exportPrisma: vi.fn(() => '') })) +vi.mock('./erd/autoInfer', () => ({ inferRelationships: vi.fn(() => []) })) +vi.mock('./components/modals', () => ({ + AddTableModal: () => null, + CardinalityModal: () => null, + EditEdgeModal: () => null, + EditTableModal: () => null, + ExportModal: () => null, + GroupModal: () => null, +})) + +vi.mock('@xyflow/react', async () => { + const React = await import('react') + return { + Background: () => null, + Controls: () => null, + MiniMap: () => null, + ReactFlow: (props: { nodes: CapturedNode[]; children?: React.ReactNode }) => { + flowCapture.renders.push(props.nodes) + return ( +
+ {props.nodes.map((node) => {String(node.data.title)})} + {props.children} +
+ ) + }, + addEdge: (edge: unknown, edges: unknown[]) => [...edges, edge], + useNodesState: (initial: CapturedNode[]) => { + const [nodes, setNodes] = React.useState(initial) + flowCapture.setNodes = setNodes as typeof flowCapture.setNodes + return [nodes, setNodes, vi.fn()] + }, + useEdgesState: (initial: unknown[]) => { + const [edges, setEdges] = React.useState(initial) + return [edges, setEdges, vi.fn()] + }, + } +}) + +const graphData = vi.hoisted(() => ({ + firstUsers: { + title: 'public.users', + columns: [{ column_name: 'id', data_type: 'bigint', is_not_null: true, is_pk: true }], + badges: { pk: true, fk: false }, + }, + firstOrders: { + title: 'public.orders', + columns: [{ column_name: 'user_id', data_type: 'bigint', is_not_null: true, is_pk: false }], + badges: { pk: false, fk: true }, + }, + secondAccounts: { + title: 'public.accounts', + columns: [{ column_name: 'account_id', data_type: 'bigint', is_not_null: true, is_pk: true }], + badges: { pk: true, fk: false }, + }, +})) + +vi.mock('./erd/convert', () => ({ + snapshotToGraph: vi.fn((snapshotJson: { marker?: string }) => snapshotJson.marker === 'second' + ? { nodes: [{ id: 'accounts', type: 'tableNode', position: { x: 0, y: 0 }, data: graphData.secondAccounts }], edges: [] } + : { + nodes: [ + { id: 'users', type: 'tableNode', position: { x: 0, y: 0 }, data: graphData.firstUsers }, + { id: 'orders', type: 'tableNode', position: { x: 200, y: 0 }, data: graphData.firstOrders }, + ], + edges: [], + }), +})) + +import App from './App' + +const projects = [{ project_space_uuid: 'project-one', project_name: 'Project One' }] +const snapshots = [ + { schema_snapshot_uuid: 'snapshot-one', status: 'running', schema_filter: null }, + { schema_snapshot_uuid: 'snapshot-two', status: 'succeeded', schema_filter: null }, +] + +function detail(id: string, marker: string, status = 'succeeded') { + return { + schema_snapshot_uuid: id, + status, + schema_filter: null, + error_message: null, + snapshot_json: { marker, relations: [], columns: [], pk_columns: [], fk_edges: [] }, + } +} + +async function renderReadyApp() { + render() + await waitFor(() => expect(api.listSnapshots).toHaveBeenCalledWith('project-one')) +} + +async function diagramOpenButtons() { + fireEvent.click(screen.getByRole('button', { name: '다이어그램' })) + return screen.findAllByRole('button', { name: '열기' }) +} + +async function openSnapshot(index: number) { + const openButtons = await diagramOpenButtons() + fireEvent.click(openButtons[index]!) + await waitFor(() => expect(api.getSnapshot).toHaveBeenCalled()) +} + +function lastNodeData(nodeId: string): Record { + const data = flowCapture.renders.at(-1)?.find((node) => node.id === nodeId)?.data + if (!data) throw new Error(`No rendered data captured for ${nodeId}`) + return data +} + +describe('App search identity and polling isolation', () => { + beforeEach(() => { + vi.clearAllMocks() + flowCapture.renders.length = 0 + flowCapture.setNodes = undefined + api.getMe.mockResolvedValue({ subject: 'user-one', display_name: 'User One' }) + api.listProjects.mockResolvedValue(projects) + api.listConnections.mockResolvedValue([]) + api.listSnapshots.mockResolvedValue(snapshots) + api.createShareLink.mockResolvedValue({ url: 'https://example.test/share' }) + }) + + afterEach(() => { + vi.useRealTimers() + cleanup() + }) + + it('preserves decorated data identity for normalized-query and position-only updates', async () => { + api.getSnapshot.mockResolvedValue(detail('snapshot-one', 'first')) + await renderReadyApp() + await openSnapshot(0) + await screen.findByText('public.users') + + const search = screen.getByLabelText('테이블 또는 컬럼 검색') + fireEvent.change(search, { target: { value: 'users' } }) + await waitFor(() => expect(lastNodeData('users').isHighlighted).toBe(true)) + const firstDecorated = lastNodeData('users') + + fireEvent.change(search, { target: { value: ' users ' } }) + await waitFor(() => expect(search).toHaveValue(' users ')) + expect(lastNodeData('users')).toBe(firstDecorated) + + await act(async () => { + flowCapture.setNodes?.((current) => current.map((node) => ( + node.id === 'users' + ? { ...node, position: { x: node.position.x + 25, y: node.position.y } } + : node + ))) + }) + expect(lastNodeData('users')).toBe(firstDecorated) + + await act(async () => { + flowCapture.setNodes?.((current) => current.map((node) => ( + node.id === 'users' ? { ...node, data: { ...node.data } } : node + ))) + }) + const replacedSourceData = lastNodeData('users') + expect(replacedSourceData).not.toBe(firstDecorated) + + fireEvent.change(search, { target: { value: 'orders' } }) + await waitFor(() => expect(lastNodeData('orders').isHighlighted).toBe(true)) + expect(lastNodeData('users')).not.toBe(replacedSourceData) + expect(graphData.firstUsers).not.toHaveProperty('isHighlighted') + }) + + it('ignores a terminal response from a superseded snapshot request', async () => { + let resolveFirst!: (value: ReturnType) => void + api.getSnapshot.mockImplementation((snapshotId: string) => { + if (snapshotId === 'snapshot-one') { + return new Promise((resolve) => { resolveFirst = resolve }) + } + return Promise.resolve(detail('snapshot-two', 'second')) + }) + + await renderReadyApp() + await openSnapshot(0) + await waitFor(() => expect(api.getSnapshot).toHaveBeenCalledWith('snapshot-one')) + + await openSnapshot(1) + await screen.findByText('public.accounts') + const refreshCountAfterCurrentTerminal = api.listSnapshots.mock.calls.length + + await act(async () => { + resolveFirst(detail('snapshot-one', 'first')) + await Promise.resolve() + }) + + expect(screen.getByText('public.accounts')).toBeInTheDocument() + expect(screen.queryByText('public.users')).not.toBeInTheDocument() + expect(api.listSnapshots).toHaveBeenCalledTimes(refreshCountAfterCurrentTerminal) + }) + + it('does not publish a stale polling rejection after the selected snapshot changes', async () => { + let rejectFirst!: (reason: Error) => void + api.getSnapshot.mockImplementation((snapshotId: string) => { + if (snapshotId === 'snapshot-one') { + return new Promise((_, reject) => { rejectFirst = reject }) + } + return Promise.resolve(detail('snapshot-two', 'second')) + }) + + await renderReadyApp() + await openSnapshot(0) + await openSnapshot(1) + await screen.findByText('public.accounts') + + await act(async () => { + rejectFirst(new Error('stale polling failure with secret detail')) + await Promise.resolve() + }) + + expect(screen.getByText('public.accounts')).toBeInTheDocument() + expect(screen.queryByText(/stale polling failure with secret detail/i)).not.toBeInTheDocument() + }) + + it('waits for each non-terminal request before scheduling the next poll', async () => { + let resolveFirst!: (value: ReturnType) => void + let resolveSecond!: (value: ReturnType) => void + api.getSnapshot + .mockImplementationOnce(() => new Promise((resolve) => { resolveFirst = resolve })) + .mockImplementationOnce(() => new Promise((resolve) => { resolveSecond = resolve })) + + await renderReadyApp() + const openButtons = await diagramOpenButtons() + vi.useFakeTimers() + + fireEvent.click(openButtons[0]!) + await act(async () => { await Promise.resolve() }) + expect(api.getSnapshot).toHaveBeenCalledTimes(1) + + await act(async () => { await vi.advanceTimersByTimeAsync(2000) }) + expect(api.getSnapshot).toHaveBeenCalledTimes(1) + + await act(async () => { + resolveFirst(detail('snapshot-one', 'first', 'running')) + await Promise.resolve() + }) + await act(async () => { await vi.advanceTimersByTimeAsync(999) }) + expect(api.getSnapshot).toHaveBeenCalledTimes(1) + await act(async () => { await vi.advanceTimersByTimeAsync(1) }) + expect(api.getSnapshot).toHaveBeenCalledTimes(2) + + await act(async () => { + resolveSecond(detail('snapshot-one', 'first')) + await Promise.resolve() + }) + await act(async () => { await vi.advanceTimersByTimeAsync(5000) }) + expect(api.getSnapshot).toHaveBeenCalledTimes(2) + }) +}) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0fa62ede3..49812e448 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -198,20 +198,30 @@ export default function App() { const searchMatchedNodeIds = useMemo(() => { return findSearchMatchedNodeIds(nodes, normalizedNodeSearch); }, [nodes, normalizedNodeSearch]); + + // ⚡ Bolt: Cache decorated search state to preserve node.data identity during 60fps drag updates + const searchCache = useMemo(() => new WeakMap(), [normalizedNodeSearch]); + const visibleNodes = useMemo(() => { if (!normalizedNodeSearch) return nodes; + return nodes.map((node) => { - const isHighlighted = searchMatchedNodeIds.has(node.id); - return { - ...node, - data: { + let cachedData = searchCache.get(node.data); + if (!cachedData) { + const isHighlighted = searchMatchedNodeIds.has(node.id); + cachedData = { ...node.data, isDimmed: !isHighlighted, isHighlighted, - }, + }; + searchCache.set(node.data, cachedData); + } + return { + ...node, + data: cachedData, }; }); - }, [nodes, normalizedNodeSearch, searchMatchedNodeIds]); + }, [nodes, normalizedNodeSearch, searchMatchedNodeIds, searchCache]); const nodeSearchStatus = normalizedNodeSearch ? `${searchMatchedNodeIds.size}개 테이블 일치` : ""; @@ -309,22 +319,46 @@ export default function App() { useEffect(() => { if (!snapshotId) return; - const timer = setInterval(() => { - getSnapshot(snapshotId) - .then((s) => { - setSnapshot(s); - if (s.status === "succeeded" || s.status === "failed" || s.status === "not_found") { - clearInterval(timer); - if (selectedProjectId) { - listSnapshots(selectedProjectId) - .then(setSnapshots) - .catch((e) => setError(String(e))); + let isCurrent = true; + let timer: number | null = null; + + async function poll() { + try { + const s = await getSnapshot(snapshotId as string); + if (!isCurrent) return; + setSnapshot(s); + + if (s.status === "succeeded" || s.status === "failed" || s.status === "not_found") { + if (selectedProjectId) { + try { + const snaps = await listSnapshots(selectedProjectId); + if (isCurrent) setSnapshots(snaps); + } catch (e) { + if (isCurrent) setError(String(e)); } } - }) - .catch((e) => setError(String(e))); - }, 1000); - return () => clearInterval(timer); + return; + } + + if (isCurrent) { + timer = window.setTimeout(poll, 1000); + } + } catch (e) { + if (isCurrent) { + setError(String(e)); + timer = window.setTimeout(poll, 1000); + } + } + } + + poll(); + + return () => { + isCurrent = false; + if (timer !== null) { + clearTimeout(timer); + } + }; }, [selectedProjectId, snapshotId]); const graph = useMemo(() => { From 183331e1054fb14b4c017e77fcd0aae99e949277 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:23:49 +0900 Subject: [PATCH 2/5] chore(deps-dev): bump vite from 8.2.0 to 8.2.1 in /frontend (#843) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.2.0 to 8.2.1. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.2.1/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 8.2.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> --- frontend/package-lock.json | 12 ++++++------ frontend/package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 891bcd09c..9acfff030 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -22,7 +22,7 @@ "fast-check": "^4.8.0", "jsdom": "^30.0.1", "typescript": "^6.0.3", - "vite": "^8.2.0", + "vite": "^8.2.1", "vitest": "^4.1.10" }, "engines": { @@ -2429,16 +2429,16 @@ } }, "node_modules/vite": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", - "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", - "postcss": "^8.5.23", - "rolldown": "~1.2.0", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "bin": { diff --git a/frontend/package.json b/frontend/package.json index f179f4570..5dcad3992 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,7 +31,7 @@ "fast-check": "^4.8.0", "jsdom": "^30.0.1", "typescript": "^6.0.3", - "vite": "^8.2.0", + "vite": "^8.2.1", "vitest": "^4.1.10" }, "overrides": { From ac3bd20f14b3e169e1f53239e47bd2d410c2cbab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 15:40:13 +0900 Subject: [PATCH 3/5] chore(deps): update cryptography requirement to 50.0.0 (#841) Align the backend dependency declaration and both hash-locked installation paths on Cryptography 50.0.0, recording the PKCS#7 oracle-resistance correction in the changelog. The unchanged exact head passed required backend/frontend, Strix, OpenCode, coverage, dependency-review, Trivy, OSV and Scorecard gates, had no unresolved review thread, and received qualifying independent approval. --- CHANGELOG.md | 1 + backend/pyproject.toml | 2 +- backend/requirements-dev.lock | 94 +++++++++++++++++------------------ backend/requirements.lock | 94 +++++++++++++++++------------------ 4 files changed, 96 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c999061c..35613431a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unreleased +- [BE] 🔒 **Cryptography 50+ 보안 경계 갱신**: `pyproject.toml`과 두 hash-locked 요구사항 파일을 동일한 Cryptography 50+ 해석으로 정합화하여 PKCS#7 오류·타이밍 구분으로 인한 CVE-2026-69247 완화를 실제 설치·검증 경로에 반영했습니다. - [FE] ⚡ **검색 노드 참조 안정화 및 순차 스냅샷 폴링**: 같은 정규화 검색어와 원본 테이블 데이터에는 장식된 `node.data` 참조를 재사용하여 드래그 중 불필요한 하위 렌더링과 할당을 줄입니다. 스냅샷 폴링은 이전 요청이 끝난 뒤에만 다음 요청을 예약하며, 선택 변경·언마운트 후 도착한 오래된 성공 또는 실패 응답을 무시합니다. - [BE] 🔒 **공유 export 전 경로 redaction**: 공개 share의 SQL / index-design / reversing-spec export에서 코멘트·`example_value`를 제거합니다. 단위 테스트로 누출을 차단합니다. - [BE] 🛠️ **함수 인덱스 중복 오탐 수정**: `lower(email)` 등 expression index를 평문 컬럼 인덱스의 중복으로 잘못 판단하지 않도록 괄호 파서를 강화했습니다. diff --git a/backend/pyproject.toml b/backend/pyproject.toml index ed076d534..b2d47dd2a 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "asyncpg>=0.31.0", "psycopg[binary]>=3.3.4", "alembic>=1.18.5", - "cryptography>=46.0.7", + "cryptography>=50.0.0", "httpx>=0.28.1", "python-jose[cryptography]>=3.5.0", "redis>=5.0.0", diff --git a/backend/requirements-dev.lock b/backend/requirements-dev.lock index a5a77b940..94ebb2dbe 100644 --- a/backend/requirements-dev.lock +++ b/backend/requirements-dev.lock @@ -544,53 +544,53 @@ coverage==7.15.2 \ --hash=sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243 \ --hash=sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a # via pytest-cov -cryptography==49.0.0 \ - --hash=sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001 \ - --hash=sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122 \ - --hash=sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6 \ - --hash=sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c \ - --hash=sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325 \ - --hash=sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69 \ - --hash=sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d \ - --hash=sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36 \ - --hash=sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc \ - --hash=sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6 \ - --hash=sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b \ - --hash=sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27 \ - --hash=sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61 \ - --hash=sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18 \ - --hash=sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db \ - --hash=sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b \ - --hash=sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb \ - --hash=sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2 \ - --hash=sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459 \ - --hash=sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e \ - --hash=sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21 \ - --hash=sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8 \ - --hash=sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7 \ - --hash=sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa \ - --hash=sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9 \ - --hash=sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db \ - --hash=sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64 \ - --hash=sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505 \ - --hash=sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5 \ - --hash=sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615 \ - --hash=sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f \ - --hash=sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866 \ - --hash=sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6 \ - --hash=sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561 \ - --hash=sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838 \ - --hash=sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9 \ - --hash=sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7 \ - --hash=sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68 \ - --hash=sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8 \ - --hash=sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3 \ - --hash=sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e \ - --hash=sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a \ - --hash=sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d \ - --hash=sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4 \ - --hash=sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493 \ - --hash=sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b +cryptography==50.0.0 \ + --hash=sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03 \ + --hash=sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7 \ + --hash=sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437 \ + --hash=sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987 \ + --hash=sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025 \ + --hash=sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037 \ + --hash=sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269 \ + --hash=sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105 \ + --hash=sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc \ + --hash=sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95 \ + --hash=sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b \ + --hash=sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47 \ + --hash=sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c \ + --hash=sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41 \ + --hash=sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c \ + --hash=sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d \ + --hash=sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7 \ + --hash=sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c \ + --hash=sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708 \ + --hash=sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef \ + --hash=sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f \ + --hash=sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f \ + --hash=sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a \ + --hash=sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f \ + --hash=sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a \ + --hash=sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a \ + --hash=sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e \ + --hash=sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3 \ + --hash=sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d \ + --hash=sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3 \ + --hash=sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f \ + --hash=sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae \ + --hash=sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30 \ + --hash=sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9 \ + --hash=sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9 \ + --hash=sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07 \ + --hash=sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba \ + --hash=sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3 \ + --hash=sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f \ + --hash=sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533 \ + --hash=sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5 \ + --hash=sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11 \ + --hash=sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9 \ + --hash=sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f \ + --hash=sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169 \ + --hash=sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645 # via # pg-erd-cloud-backend (backend/pyproject.toml) # python-jose diff --git a/backend/requirements.lock b/backend/requirements.lock index a333302c7..6b0a1ef79 100644 --- a/backend/requirements.lock +++ b/backend/requirements.lock @@ -405,53 +405,53 @@ charset-normalizer==3.4.9 \ --hash=sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf \ --hash=sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115 # via requests -cryptography==49.0.0 \ - --hash=sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001 \ - --hash=sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122 \ - --hash=sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6 \ - --hash=sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c \ - --hash=sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325 \ - --hash=sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69 \ - --hash=sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d \ - --hash=sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36 \ - --hash=sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc \ - --hash=sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6 \ - --hash=sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b \ - --hash=sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27 \ - --hash=sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61 \ - --hash=sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18 \ - --hash=sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db \ - --hash=sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b \ - --hash=sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb \ - --hash=sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2 \ - --hash=sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459 \ - --hash=sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e \ - --hash=sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21 \ - --hash=sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8 \ - --hash=sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7 \ - --hash=sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa \ - --hash=sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9 \ - --hash=sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db \ - --hash=sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64 \ - --hash=sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505 \ - --hash=sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5 \ - --hash=sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615 \ - --hash=sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f \ - --hash=sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866 \ - --hash=sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6 \ - --hash=sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561 \ - --hash=sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838 \ - --hash=sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9 \ - --hash=sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7 \ - --hash=sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68 \ - --hash=sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8 \ - --hash=sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3 \ - --hash=sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e \ - --hash=sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a \ - --hash=sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d \ - --hash=sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4 \ - --hash=sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493 \ - --hash=sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b +cryptography==50.0.0 \ + --hash=sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03 \ + --hash=sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7 \ + --hash=sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437 \ + --hash=sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987 \ + --hash=sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025 \ + --hash=sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037 \ + --hash=sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269 \ + --hash=sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105 \ + --hash=sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc \ + --hash=sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95 \ + --hash=sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b \ + --hash=sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47 \ + --hash=sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c \ + --hash=sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41 \ + --hash=sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c \ + --hash=sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d \ + --hash=sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7 \ + --hash=sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c \ + --hash=sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708 \ + --hash=sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef \ + --hash=sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f \ + --hash=sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f \ + --hash=sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a \ + --hash=sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f \ + --hash=sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a \ + --hash=sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a \ + --hash=sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e \ + --hash=sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3 \ + --hash=sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d \ + --hash=sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3 \ + --hash=sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f \ + --hash=sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae \ + --hash=sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30 \ + --hash=sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9 \ + --hash=sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9 \ + --hash=sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07 \ + --hash=sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba \ + --hash=sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3 \ + --hash=sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f \ + --hash=sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533 \ + --hash=sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5 \ + --hash=sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11 \ + --hash=sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9 \ + --hash=sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f \ + --hash=sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169 \ + --hash=sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645 # via # pg-erd-cloud-backend (backend/pyproject.toml) # python-jose From 4bdd832f670f1abec1d16a30388da3542b721c65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:02:47 +0000 Subject: [PATCH 4/5] chore(deps): bump step-security/harden-runner from 2.19.4 to 2.20.1 (#844) Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.19.4 to 2.20.1. - [Release notes](https://github.com/step-security/harden-runner/releases) - [Commits](https://github.com/step-security/harden-runner/compare/9af89fc71515a100421586dfdb3dc9c984fbf411...b09bb98e06d4d774595224525879c09bc6e98c40) --- updated-dependencies: - dependency-name: step-security/harden-runner dependency-version: 2.20.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c235f0de..153677e9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden runner - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 with: egress-policy: audit @@ -47,7 +47,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden runner - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 with: egress-policy: audit From 466e616e96fc3069a5022351db35fa378b342d64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 11:50:31 +0000 Subject: [PATCH 5/5] chore(deps-dev): bump @types/react from 19.2.17 to 19.2.18 in /frontend (#839) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.2.17 to 19.2.18. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-version: 19.2.18 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9acfff030..ee2736077 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,7 +16,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/react": "^19.2.10", + "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@vitest/coverage-v8": "^4.1.9", "fast-check": "^4.8.0", @@ -898,9 +898,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 5dcad3992..14cc671ff 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/react": "^19.2.10", + "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@vitest/coverage-v8": "^4.1.9", "fast-check": "^4.8.0",