Skip to content
Merged
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Vagrantfile

### conventions ###
venv/
.venv/
venv-*/
v/

Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
FROM node:lts as build
LABEL org.opencontainers.image.source = "https://github.com/OWASP/OpenCRE"
WORKDIR /code
RUN apt-get update && apt-get install -y chromium
COPY . /code
RUN yarn install && yarn build

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const ExplorerForceGraph = () => {
fullLoadProgress,
dataLoadError,
} = useDataStore();
const fgRef = useRef<ForceGraphMethods>();
const fgRef = useRef<ForceGraphMethods | undefined>(undefined);
// ADDING STATE FOR FILTERING LOGIC
const [filterTypeA, setFilterTypeA] = useState('');
const [filterTypeB, setFilterTypeB] = useState('');
Expand Down
6 changes: 4 additions & 2 deletions application/frontend/src/pages/GapAnalysis/GapAnalysis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const GapAnalysis = () => {
const [loadingGA, setLoadingGA] = useState<boolean>(false);
const [error, setError] = useState<string | null | object>(null);
const { apiUrl } = useEnvironment();
const timerIdRef = useRef<NodeJS.Timer>();
const timerIdRef = useRef<ReturnType<typeof setInterval> | undefined>(undefined);

useEffect(() => {
const fetchData = async () => {
Expand Down Expand Up @@ -173,7 +173,9 @@ export const GapAnalysis = () => {
timerIdRef.current = setInterval(pollingCallback, 10000);
};
const stopPolling = () => {
clearInterval(timerIdRef.current);
if (timerIdRef.current !== undefined) {
clearInterval(timerIdRef.current);
}
};

if (gaJob) {
Expand Down
2 changes: 1 addition & 1 deletion application/frontend/src/pages/chatbot/chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const Chatbot = () => {

function processResponse(response: string) {
const responses = response.split('```');
const res: JSX.Element[] = [];
const res: React.ReactElement[] = [];

responses.forEach((txt, i) => {
if (i % 2 === 0) {
Expand Down
4 changes: 2 additions & 2 deletions application/frontend/src/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactNode } from 'react';
import { ElementType } from 'react';

import {
BROWSEROOT,
Expand Down Expand Up @@ -34,7 +34,7 @@ const ExplorerForceGraphWithLayout = withExplorerLayout(ExplorerForceGraph);

export interface IRoute {
path: string;
component: ReactNode | ReactNode[];
component: ElementType;
showFilter: boolean;
}
export interface Capabilities {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"build": "webpack --config webpack.prod.js",
"start": "webpack serve",
"test:e2e": "jest",
"postinstall": "del-cli node_modules/@types/react-dom/node_modules/@types && del-cli node_modules/webpack/types.d.ts"
"postinstall": "del-cli node_modules/@types/react-dom/node_modules/@types && del-cli node_modules/webpack/types.d.ts && del-cli node_modules/@types/minimatch"
},
"keywords": [],
"author": "Craig Knott",
Expand Down
16 changes: 14 additions & 2 deletions scripts/prod-docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@

export INSECURE_REQUESTS=1
export FLASK_CONFIG="production"
export FLASK_APP=`pwd`/cre.py
flask db upgrade
export FLASK_APP=`pwd`/cre.py
flask db upgrade heads
python - <<'PY'
import sqlite3

db_path = "/code/standards_cache.sqlite"
conn = sqlite3.connect(db_path)
for table in ("cre", "node"):
cols = {row[1] for row in conn.execute(f"PRAGMA table_info({table})")}
if "document_metadata" not in cols:
conn.execute(f"ALTER TABLE {table} ADD COLUMN document_metadata JSON")
conn.commit()
conn.close()
PY
Comment on lines +7 to +18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle missing tables and add set -e to prevent serving a broken app.

Two stability risks in this block:

  1. If the cre or node tables don't exist in standards_cache.sqlite (e.g., fresh database or flask db upgrade heads failed silently), PRAGMA table_info returns an empty set, "document_metadata" not in cols is True, and ALTER TABLE cre ADD COLUMN ... throws sqlite3.OperationalError: no such table. The exception is unhandled.

  2. The script has no set -e, so both the migration failure (line 6) and the Python crash are silently swallowed — gunicorn starts and serves an app with a broken database schema.

Consider guarding the ALTER TABLE with a table-existence check and adding set -e to the script so migration or schema-patch failures halt startup instead of silently serving a broken app.

🛡️ Proposed fix: guard ALTER TABLE and add set -e
 #! /bin/bash
 
+set -e
+
 export INSECURE_REQUESTS=1
 export FLASK_CONFIG="production"
 export FLASK_APP=`pwd`/cre.py
 flask db upgrade heads
 python - <<'PY'
 import sqlite3
 
 db_path = "/code/standards_cache.sqlite"
 conn = sqlite3.connect(db_path)
 for table in ("cre", "node"):
     cols = {row[1] for row in conn.execute(f"PRAGMA table_info({table})")}
-    if "document_metadata" not in cols:
+    if cols and "document_metadata" not in cols:
         conn.execute(f"ALTER TABLE {table} ADD COLUMN document_metadata JSON")
 conn.commit()
 conn.close()
 PY
🤖 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 `@scripts/prod-docker-entrypoint.sh` around lines 7 - 18, Add a startup failure
guard in the prod entrypoint so schema patching never silently continues on
error: the Python block that opens sqlite3.connect(db_path) and loops over
cre/node should first verify each table exists before calling PRAGMA table_info
and ALTER TABLE, and skip or fail cleanly if it does not. Also add set -e at the
top of the script so any failed migration or Python exception stops startup
instead of letting gunicorn launch with a broken database schema.

python /code/cre.py --upstream_sync
gunicorn cre:app -b :5000 --timeout 90
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
"esModuleInterop": true
},
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "src/**/*"],
"exclude": ["venv", "dist", "node_modules", "build", "scripts"]
"exclude": ["venv", "dist", "node_modules", "build", "scripts", "application/frontend/www"]
}
Loading