From 62dd1819cbe76027f1671cf5ac5667143cc403f1 Mon Sep 17 00:00:00 2001 From: jp170na Date: Fri, 14 Aug 2026 23:45:32 +0200 Subject: [PATCH] security --- app/security.py | 109 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/app/security.py b/app/security.py index 54c775b..12ac494 100644 --- a/app/security.py +++ b/app/security.py @@ -5,7 +5,11 @@ import hmac import os from typing import Any -from fastapi import HTTPException, Security, status +from fastapi import ( + HTTPException, + Security, + status, +) from fastapi.security import ( APIKeyHeader, HTTPAuthorizationCredentials, @@ -19,6 +23,21 @@ SEARCH_API_KEY_HEADER = "X-API-Key" SYNC_API_KEY_HEADER = "X-API-Key" +TRUE_ENV_VALUES = { + "1", + "true", + "yes", + "on", +} + +FALSE_ENV_VALUES = { + "0", + "false", + "no", + "off", +} + + search_api_key_scheme = APIKeyHeader( name=SEARCH_API_KEY_HEADER, scheme_name="SearchApiKey", @@ -87,7 +106,26 @@ def expected_gitea_repository() -> str: "EXPECTED_GITEA_REPOSITORY" ) - if "/" not in value: + parts = value.split( + "/" + ) + + if len(parts) != 2: + raise RuntimeError( + "EXPECTED_GITEA_REPOSITORY musí mať " + "tvar vlastník/repozitár" + ) + + owner, repository = parts + + if ( + not owner + or not repository + or any( + character.isspace() + for character in value + ) + ): raise RuntimeError( "EXPECTED_GITEA_REPOSITORY musí mať " "tvar vlastník/repozitár" @@ -102,26 +140,53 @@ def webhook_should_pull_git() -> bool: "false", ).strip().casefold() - return value in { - "1", - "true", - "yes", - "on", - } + if value in TRUE_ENV_VALUES: + return True + + if value in FALSE_ENV_VALUES: + return False + + raise RuntimeError( + "WEBHOOK_PULL_GIT musí byť boolean " + "hodnota true/false" + ) def validate_security_configuration() -> None: - validate_secret( + webhook_secret = validate_secret( "WEBHOOK_SECRET" ) - validate_secret( + + sync_api_key = validate_secret( "SYNC_API_KEY" ) - validate_secret( + + search_api_key = validate_secret( "SEARCH_API_KEY" ) + + # Každá funkcia musí používať vlastný + # credential. Jeden uniknutý secret tak + # neposkytne prístup ku všetkým operáciám. + if len( + { + webhook_secret, + sync_api_key, + search_api_key, + } + ) != 3: + raise RuntimeError( + "WEBHOOK_SECRET, SYNC_API_KEY " + "a SEARCH_API_KEY musia byť " + "navzájom rozdielne" + ) + expected_gitea_repository() + # Validujeme aj voliteľnú boolean + # konfiguráciu už pri štarte aplikácie. + webhook_should_pull_git() + def require_search_api_key( api_key: str | None = Security( @@ -272,13 +337,16 @@ def repository_name_from_payload( ): return None - value = ( - repository.get( - "full_name" - ) - or repository.get( - "name" - ) + # Pri webhooku akceptujeme iba + # jednoznačný Gitea full_name: + # + # vlastník/repozitár + # + # Samotné "name" nestačí, pretože + # rovnaký názov môže existovať pod + # rôznymi vlastníkmi. + value = repository.get( + "full_name" ) if not isinstance( @@ -297,7 +365,6 @@ def same_repository( expected: str, ) -> bool: return hmac.compare_digest( - actual.casefold(), - expected.casefold(), + actual.strip().casefold(), + expected.strip().casefold(), ) -