Skip to content

Commit cdd6680

Browse files
committed
chore: Update CodeQL configuration and improve linting workflow
- Added a new CodeQL configuration file to ignore paths in the frontend build directory. - Updated the CodeQL workflow to reference the new configuration file for better analysis. - Enhanced the Ruff linting workflow by adding permissions and ensuring consistent formatting. These changes improve the quality assurance processes by refining static analysis and linting configurations.
1 parent 08930fb commit cdd6680

47 files changed

Lines changed: 724 additions & 534 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/codeql/codeql-config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
paths-ignore:
2+
- frontend/build/**

.github/workflows/codeql.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ jobs:
7373
with:
7474
languages: ${{ matrix.language }}
7575
build-mode: ${{ matrix.build-mode }}
76+
config-file: ./.github/codeql/codeql-config.yml
7677
# If you wish to specify custom queries, you can do so here or in a config file.
7778
# By default, queries listed here will override any specified in a config file.
7879
# Prefix the list here with "+" to use these queries and those in the config file.

.github/workflows/ruff.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
pull_request:
77
branches: [ develop ]
88

9+
permissions:
10+
contents: read
11+
912
jobs:
1013
lint:
1114
name: Run Ruff
@@ -26,4 +29,4 @@ jobs:
2629
2730
- name: Run Ruff formatter
2831
run: |
29-
make format
32+
make format

backend/app/api/ogc/endpoints.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ async def get_item(
135135

136136
try:
137137
resource_response = await search_service.get_resource(recordId)
138-
except HTTPException:
138+
except HTTPException as e:
139+
if e.status_code >= 500:
140+
raise HTTPException(status_code=500, detail="Internal server error") from e
139141
raise
140142
except Exception as e:
141143
logger.error(f"Error fetching resource {recordId} for OGC endpoint", exc_info=True)

backend/app/api/v1/admin.py

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ async def clear_cache(
4646
try:
4747
result = await service.clear_cache(cache_type)
4848
return create_response(result)
49-
except CacheManagementError as e:
50-
logger.error(f"Cache management error: {str(e)}")
51-
return create_response({"error": str(e)}, status_code=500)
52-
except Exception as e:
53-
logger.error(f"Unexpected error clearing cache: {str(e)}")
54-
return create_response({"error": f"Failed to clear cache: {str(e)}"}, status_code=500)
49+
except CacheManagementError:
50+
logger.error("Cache management error while clearing cache", exc_info=True)
51+
return create_response({"error": "Failed to clear cache"}, status_code=500)
52+
except Exception:
53+
logger.error("Unexpected error clearing cache", exc_info=True)
54+
return create_response({"error": "Failed to clear cache"}, status_code=500)
5555

5656

5757
@router.post("/reindex")
@@ -64,15 +64,11 @@ async def reindex(
6464
result = await service.reindex_resources()
6565
return create_response(result, callback)
6666
except ReindexingError as e:
67-
logger.error(f"Reindexing error: {str(e)}")
68-
raise HTTPException(
69-
status_code=500, detail={"message": "Reindexing failed", "error": str(e)}
70-
) from e
67+
logger.error("Reindexing error", exc_info=True)
68+
raise HTTPException(status_code=500, detail={"message": "Reindexing failed"}) from e
7169
except Exception as e:
72-
logger.error(f"Unexpected error during reindexing: {str(e)}")
73-
raise HTTPException(
74-
status_code=500, detail={"message": "Reindexing failed", "error": str(e)}
75-
) from e
70+
logger.error("Unexpected error during reindexing", exc_info=True)
71+
raise HTTPException(status_code=500, detail={"message": "Reindexing failed"}) from e
7672

7773

7874
@router.post("/resources/{id}/summarize")
@@ -97,14 +93,18 @@ async def summarize_resource(
9793
sanitized_response = sanitize_for_json(result)
9894
return create_response(sanitized_response, callback)
9995
except ResourceNotFoundError as e:
100-
logger.error(f"Resource not found: {str(e)}")
101-
raise HTTPException(status_code=404, detail=str(e)) from e
96+
logger.error("Resource not found while summarizing %s", id, exc_info=True)
97+
raise HTTPException(status_code=404, detail="Resource not found") from e
10298
except ResourceProcessingError as e:
103-
logger.error(f"Resource processing error: {str(e)}")
104-
raise HTTPException(status_code=500, detail=str(e)) from e
99+
logger.error("Resource processing error while summarizing %s", id, exc_info=True)
100+
raise HTTPException(status_code=500, detail="Failed to start summary generation") from e
105101
except Exception as e:
106-
logger.error(f"Unexpected error triggering summary generation for resource {id}: {str(e)}")
107-
raise HTTPException(status_code=500, detail=str(e)) from e
102+
logger.error(
103+
"Unexpected error triggering summary generation for resource %s",
104+
id,
105+
exc_info=True,
106+
)
107+
raise HTTPException(status_code=500, detail="Failed to start summary generation") from e
108108

109109

110110
@router.post("/resources/{id}/identify-geo-entities")
@@ -125,14 +125,27 @@ async def identify_geo_entities(
125125
result = await service.identify_geo_entities(id)
126126
return create_response(result, callback)
127127
except ResourceNotFoundError as e:
128-
logger.error(f"Resource not found: {str(e)}")
129-
raise HTTPException(status_code=404, detail=str(e)) from e
128+
logger.error(
129+
"Resource not found while identifying geographic entities for %s",
130+
id,
131+
exc_info=True,
132+
)
133+
raise HTTPException(status_code=404, detail="Resource not found") from e
130134
except ResourceProcessingError as e:
131-
logger.error(f"Resource processing error: {str(e)}")
132-
raise HTTPException(status_code=500, detail=str(e)) from e
135+
logger.error(
136+
"Resource processing error while identifying geographic entities for %s",
137+
id,
138+
exc_info=True,
139+
)
140+
raise HTTPException(
141+
status_code=500, detail="Failed to start geographic entity identification"
142+
) from e
133143
except Exception as e:
134144
logger.error(
135-
f"Unexpected error triggering geographic entity identification "
136-
f"for resource {id}: {str(e)}"
145+
"Unexpected error triggering geographic entity identification for resource %s",
146+
id,
147+
exc_info=True,
137148
)
138-
raise HTTPException(status_code=500, detail=str(e)) from e
149+
raise HTTPException(
150+
status_code=500, detail="Failed to start geographic entity identification"
151+
) from e

backend/app/api/v1/endpoint_modules/admin.py

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from app.api.v1.auth import verify_credentials
1313
from app.api.v1.utils import create_response, sanitize_for_json
14+
from app.security_utils import require_safe_filename
1415
from app.services.admin_service import (
1516
AdminService,
1617
CacheManagementError,
@@ -116,12 +117,12 @@ async def clear_cache(
116117
try:
117118
result = await service.clear_cache(cache_type)
118119
return create_response(result)
119-
except CacheManagementError as e:
120-
logger.error(f"Cache management error: {str(e)}")
121-
return create_response({"error": str(e)}, status_code=500)
122-
except Exception as e:
123-
logger.error(f"Unexpected error clearing cache: {str(e)}")
124-
return create_response({"error": f"Failed to clear cache: {str(e)}"}, status_code=500)
120+
except CacheManagementError:
121+
logger.error("Cache management error while clearing cache", exc_info=True)
122+
return create_response({"error": "Failed to clear cache"}, status_code=500)
123+
except Exception:
124+
logger.error("Unexpected error clearing cache", exc_info=True)
125+
return create_response({"error": "Failed to clear cache"}, status_code=500)
125126

126127

127128
class CachePurgeRequest(BaseModel):
@@ -162,8 +163,8 @@ async def purge_cache(
162163
except HTTPException:
163164
raise
164165
except Exception as e:
165-
logger.error(f"Unexpected error purging cache: {e}", exc_info=True)
166-
raise HTTPException(status_code=500, detail=str(e)) from e
166+
logger.error("Unexpected error purging cache", exc_info=True)
167+
raise HTTPException(status_code=500, detail="Failed to purge cache") from e
167168

168169

169170
@router.post("/reindex")
@@ -176,15 +177,11 @@ async def reindex(
176177
result = await service.reindex_resources()
177178
return create_response(result, callback)
178179
except ReindexingError as e:
179-
logger.error(f"Reindexing error: {str(e)}")
180-
raise HTTPException(
181-
status_code=500, detail={"message": "Reindexing failed", "error": str(e)}
182-
) from e
180+
logger.error("Reindexing error", exc_info=True)
181+
raise HTTPException(status_code=500, detail={"message": "Reindexing failed"}) from e
183182
except Exception as e:
184-
logger.error(f"Unexpected error during reindexing: {str(e)}")
185-
raise HTTPException(
186-
status_code=500, detail={"message": "Reindexing failed", "error": str(e)}
187-
) from e
183+
logger.error("Unexpected error during reindexing", exc_info=True)
184+
raise HTTPException(status_code=500, detail={"message": "Reindexing failed"}) from e
188185

189186

190187
@router.post("/resources/{id}/summarize")
@@ -209,14 +206,18 @@ async def summarize_resource(
209206
sanitized_response = sanitize_for_json(result)
210207
return create_response(sanitized_response, callback)
211208
except ResourceNotFoundError as e:
212-
logger.error(f"Resource not found: {str(e)}")
213-
raise HTTPException(status_code=404, detail=str(e)) from e
209+
logger.error("Resource not found while summarizing %s", id, exc_info=True)
210+
raise HTTPException(status_code=404, detail="Resource not found") from e
214211
except ResourceProcessingError as e:
215-
logger.error(f"Resource processing error: {str(e)}")
216-
raise HTTPException(status_code=500, detail=str(e)) from e
212+
logger.error("Resource processing error while summarizing %s", id, exc_info=True)
213+
raise HTTPException(status_code=500, detail="Failed to start summary generation") from e
217214
except Exception as e:
218-
logger.error(f"Unexpected error triggering summary generation for resource {id}: {str(e)}")
219-
raise HTTPException(status_code=500, detail=str(e)) from e
215+
logger.error(
216+
"Unexpected error triggering summary generation for resource %s",
217+
id,
218+
exc_info=True,
219+
)
220+
raise HTTPException(status_code=500, detail="Failed to start summary generation") from e
220221

221222

222223
@router.post("/resources/{id}/identify-geo-entities")
@@ -237,17 +238,30 @@ async def identify_geo_entities(
237238
result = await service.identify_geo_entities(id)
238239
return create_response(result, callback)
239240
except ResourceNotFoundError as e:
240-
logger.error(f"Resource not found: {str(e)}")
241-
raise HTTPException(status_code=404, detail=str(e)) from e
241+
logger.error(
242+
"Resource not found while identifying geographic entities for %s",
243+
id,
244+
exc_info=True,
245+
)
246+
raise HTTPException(status_code=404, detail="Resource not found") from e
242247
except ResourceProcessingError as e:
243-
logger.error(f"Resource processing error: {str(e)}")
244-
raise HTTPException(status_code=500, detail=str(e)) from e
248+
logger.error(
249+
"Resource processing error while identifying geographic entities for %s",
250+
id,
251+
exc_info=True,
252+
)
253+
raise HTTPException(
254+
status_code=500, detail="Failed to start geographic entity identification"
255+
) from e
245256
except Exception as e:
246257
logger.error(
247-
f"Unexpected error triggering geographic entity identification "
248-
f"for resource {id}: {str(e)}"
258+
"Unexpected error triggering geographic entity identification for resource %s",
259+
id,
260+
exc_info=True,
249261
)
250-
raise HTTPException(status_code=500, detail=str(e)) from e
262+
raise HTTPException(
263+
status_code=500, detail="Failed to start geographic entity identification"
264+
) from e
251265

252266

253267
# API Key Management Endpoints
@@ -279,8 +293,8 @@ async def create_api_key(
279293
except HTTPException:
280294
raise
281295
except Exception as e:
282-
logger.error(f"Error creating API key: {str(e)}")
283-
raise HTTPException(status_code=500, detail=str(e)) from e
296+
logger.error("Error creating API key", exc_info=True)
297+
raise HTTPException(status_code=500, detail="Failed to create API key") from e
284298

285299

286300
@router.get("/api-keys")
@@ -290,8 +304,8 @@ async def list_api_keys():
290304
keys = await api_key_service.list_api_keys()
291305
return create_response({"keys": keys})
292306
except Exception as e:
293-
logger.error(f"Error listing API keys: {str(e)}")
294-
raise HTTPException(status_code=500, detail=str(e)) from e
307+
logger.error("Error listing API keys", exc_info=True)
308+
raise HTTPException(status_code=500, detail="Failed to list API keys") from e
295309

296310

297311
@router.patch("/api-keys/{key_id}")
@@ -329,8 +343,8 @@ async def update_api_key(
329343
except HTTPException:
330344
raise
331345
except Exception as e:
332-
logger.error(f"Error updating API key: {str(e)}")
333-
raise HTTPException(status_code=500, detail=str(e)) from e
346+
logger.error("Error updating API key", exc_info=True)
347+
raise HTTPException(status_code=500, detail="Failed to update API key") from e
334348

335349

336350
@router.delete("/api-keys/{key_id}")
@@ -348,8 +362,8 @@ async def revoke_api_key(key_id: int):
348362
except HTTPException:
349363
raise
350364
except Exception as e:
351-
logger.error(f"Error revoking API key: {str(e)}")
352-
raise HTTPException(status_code=500, detail=str(e)) from e
365+
logger.error("Error revoking API key", exc_info=True)
366+
raise HTTPException(status_code=500, detail="Failed to revoke API key") from e
353367

354368

355369
@router.get("/api-tiers")
@@ -359,8 +373,8 @@ async def list_api_tiers():
359373
tiers = await api_key_service.list_tiers()
360374
return create_response({"tiers": tiers})
361375
except Exception as e:
362-
logger.error(f"Error listing API tiers: {str(e)}")
363-
raise HTTPException(status_code=500, detail=str(e)) from e
376+
logger.error("Error listing API tiers", exc_info=True)
377+
raise HTTPException(status_code=500, detail="Failed to list API tiers") from e
364378

365379

366380
# --- OpenGeoMetadata (OGM) admin endpoints ---
@@ -818,20 +832,26 @@ async def download_ogm_dump_file(run_id: int, filename: str):
818832
if not run or not run.get("ogm_dump_dir"):
819833
raise HTTPException(status_code=404, detail="Dump not found for run")
820834

835+
try:
836+
safe_filename = require_safe_filename(filename)
837+
except ValueError as exc:
838+
raise HTTPException(status_code=400, detail="Invalid filename") from exc
839+
821840
dump_dir = Path(run["ogm_dump_dir"])
841+
resolved_dump_dir = dump_dir.resolve()
822842
# Prevent path traversal
823-
candidate = (dump_dir / filename).resolve()
824-
if dump_dir.resolve() not in candidate.parents and candidate != dump_dir.resolve():
843+
candidate = (resolved_dump_dir / safe_filename).resolve()
844+
if resolved_dump_dir not in candidate.parents:
825845
raise HTTPException(status_code=400, detail="Invalid filename")
826846

827847
if not candidate.exists() or not candidate.is_file():
828848
raise HTTPException(status_code=404, detail="File not found")
829849

830850
# Basic content type mapping
831851
media_type = "application/octet-stream"
832-
if filename.endswith(".json") or filename.endswith(".ndjson"):
852+
if safe_filename.endswith(".json") or safe_filename.endswith(".ndjson"):
833853
media_type = "application/json"
834-
elif filename.endswith(".parquet"):
854+
elif safe_filename.endswith(".parquet"):
835855
media_type = "application/octet-stream"
836856

837-
return FileResponse(str(candidate), media_type=media_type, filename=filename)
857+
return FileResponse(str(candidate), media_type=media_type, filename=safe_filename)

backend/app/api/v1/endpoint_modules/resources/citation.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ async def get_resource_citation(
5757
}
5858

5959
return create_response(response_payload, callback)
60-
except Exception as e:
61-
logger.error(f"Error getting citation for resource {id}: {str(e)}", exc_info=True)
62-
return JSONResponse(content={"error": str(e)}, status_code=500)
60+
except Exception:
61+
logger.error("Error getting citation for resource %s", id, exc_info=True)
62+
return JSONResponse(content={"error": "Failed to get citation"}, status_code=500)
6363

6464

6565
@router.get("/resources/{id}/citation/json-ld")
@@ -81,9 +81,9 @@ async def get_resource_citation_json_ld(id: str):
8181
)
8282
ld = service.to_json_ld(id)
8383
return JSONResponse(content=ld, media_type="application/ld+json")
84-
except Exception as e:
85-
logger.error(f"Error getting JSON-LD for resource {id}: {str(e)}", exc_info=True)
86-
return JSONResponse(content={"error": str(e)}, status_code=500)
84+
except Exception:
85+
logger.error("Error getting JSON-LD for resource %s", id, exc_info=True)
86+
return JSONResponse(content={"error": "Failed to get citation metadata"}, status_code=500)
8787

8888

8989
@router.get("/resources/{id}/citation/ris")
@@ -109,9 +109,9 @@ async def get_resource_citation_ris(id: str):
109109
media_type="application/x-research-info-systems",
110110
headers={"Content-Disposition": f'attachment; filename="{id}.ris"'},
111111
)
112-
except Exception as e:
113-
logger.error(f"Error getting RIS for resource {id}: {str(e)}", exc_info=True)
114-
return JSONResponse(content={"error": str(e)}, status_code=500)
112+
except Exception:
113+
logger.error("Error getting RIS for resource %s", id, exc_info=True)
114+
return JSONResponse(content={"error": "Failed to get RIS citation"}, status_code=500)
115115

116116

117117
@router.get("/resources/{id}/citation/bibtex")
@@ -137,6 +137,6 @@ async def get_resource_citation_bibtex(id: str):
137137
media_type="application/x-bibtex",
138138
headers={"Content-Disposition": f'attachment; filename="{id}.bib"'},
139139
)
140-
except Exception as e:
141-
logger.error(f"Error getting BibTeX for resource {id}: {str(e)}", exc_info=True)
142-
return JSONResponse(content={"error": str(e)}, status_code=500)
140+
except Exception:
141+
logger.error("Error getting BibTeX for resource %s", id, exc_info=True)
142+
return JSONResponse(content={"error": "Failed to get BibTeX citation"}, status_code=500)

0 commit comments

Comments
 (0)