Sanity Checks für Ordner und Dateinamen hinzugefügt
This commit is contained in:
@@ -42,20 +42,40 @@ create or replace package body pck_net_storage as
|
|||||||
end if;
|
end if;
|
||||||
end f_build_url;
|
end f_build_url;
|
||||||
|
|
||||||
|
-- Normalisiert einen Ordnerpfad: stellt sicher, dass er mit / endet.
|
||||||
|
-- null bleibt null (= Bucket-Root).
|
||||||
|
function f_normalize_prefix (i_prefix in varchar2) return varchar2
|
||||||
|
is
|
||||||
|
begin
|
||||||
|
if i_prefix is null
|
||||||
|
then
|
||||||
|
return null;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
return rtrim(i_prefix, '/') || '/';
|
||||||
|
end f_normalize_prefix;
|
||||||
|
|
||||||
procedure p_assert_allowed (i_object_key in varchar2)
|
procedure p_assert_allowed (i_object_key in varchar2)
|
||||||
/*Kopf------------------------------------------------------------------------------------------------
|
/*Kopf------------------------------------------------------------------------------------------------
|
||||||
-- Beschreibung: Prüft den Objektschlüssel auf Path-Traversal-Angriffe und Tenant-Scope.
|
-- Beschreibung: Prüft den Objektschlüssel auf Gültigkeit, Path-Traversal-Angriffe und Tenant-Scope.
|
||||||
-- Wirft Application Error -20004 bei Path Traversal, -20005 bei Scope-Verletzung.
|
-- Wirft Application Error -20008 bei null-Key, -20004 bei Path Traversal,
|
||||||
|
-- -20005 bei Scope-Verletzung.
|
||||||
------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------
|
||||||
-- Parameter: i_object_key Zu prüfender Objektschlüssel
|
-- Parameter: i_object_key Zu prüfender Objektschlüssel
|
||||||
------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------
|
||||||
-- MA Datum Änderung
|
-- MA Datum Änderung
|
||||||
-- SCK 2026-04-08 Prozedur erstellt
|
-- SCK 2026-04-08 Prozedur erstellt
|
||||||
|
-- SCK 2026-04-10 Null-Prüfung und führender-Slash-Check ergänzt
|
||||||
------------------------------------------------------------------------------------------------Kopf*/
|
------------------------------------------------------------------------------------------------Kopf*/
|
||||||
is
|
is
|
||||||
l_tenant_prefix varchar2(256);
|
l_tenant_prefix varchar2(256);
|
||||||
begin
|
begin
|
||||||
if instr(i_object_key, '..') > 0
|
if i_object_key is null
|
||||||
|
then
|
||||||
|
raise_application_error(-20008, 'Object key darf nicht null sein');
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if instr(i_object_key, '..') > 0 or substr(i_object_key, 1, 1) = '/'
|
||||||
then
|
then
|
||||||
raise_application_error(-20004, 'Path traversal attempt detected');
|
raise_application_error(-20004, 'Path traversal attempt detected');
|
||||||
end if;
|
end if;
|
||||||
@@ -159,20 +179,21 @@ create or replace package body pck_net_storage as
|
|||||||
-- SCK 2026-04-08 Funktion erstellt
|
-- SCK 2026-04-08 Funktion erstellt
|
||||||
------------------------------------------------------------------------------------------------Kopf*/
|
------------------------------------------------------------------------------------------------Kopf*/
|
||||||
is
|
is
|
||||||
l_url varchar2(4000);
|
l_url varchar2(4000);
|
||||||
l_response clob;
|
l_response clob;
|
||||||
l_result t_net_storage_tab := t_net_storage_tab();
|
l_result t_net_storage_tab := t_net_storage_tab();
|
||||||
l_next_start varchar2(1024);
|
l_next_start varchar2(1024);
|
||||||
l_count number := 0;
|
l_count number := 0;
|
||||||
l_done boolean := false;
|
l_done boolean := false;
|
||||||
l_cur_start varchar2(1024) := i_start_with;
|
l_cur_start varchar2(1024) := i_start_with;
|
||||||
c_page_size constant number := 1000;
|
l_parent_folder varchar2(1024) := f_normalize_prefix(i_parent_folder);
|
||||||
|
c_page_size constant number := 1000;
|
||||||
begin
|
begin
|
||||||
while not l_done
|
while not l_done
|
||||||
loop
|
loop
|
||||||
l_url := f_build_url()
|
l_url := f_build_url()
|
||||||
|| '?limit=' || c_page_size
|
|| '?limit=' || c_page_size
|
||||||
|| (case when i_parent_folder is not null then '&prefix=' || utl_url.escape(i_parent_folder, false) else '' end)
|
|| (case when l_parent_folder is not null then '&prefix=' || utl_url.escape(l_parent_folder, false) else '' end)
|
||||||
|| (case when i_include_subfolders = 'N' then '&delimiter=/' else '' end);
|
|| (case when i_include_subfolders = 'N' then '&delimiter=/' else '' end);
|
||||||
|
|
||||||
if l_cur_start is not null
|
if l_cur_start is not null
|
||||||
@@ -202,7 +223,8 @@ create or replace package body pck_net_storage as
|
|||||||
rec.object_name
|
rec.object_name
|
||||||
,rec.object_size
|
,rec.object_size
|
||||||
,to_date(substr(rec.last_modified, 1, 19), 'YYYY-MM-DD"T"HH24:MI:SS')
|
,to_date(substr(rec.last_modified, 1, 19), 'YYYY-MM-DD"T"HH24:MI:SS')
|
||||||
,'N'
|
-- Explizit angelegte Ordner sind Zero-Byte-Objekte mit trailing /
|
||||||
|
,(case when rec.object_name like '%/' then 'Y' else 'N' end)
|
||||||
,rec.etag
|
,rec.etag
|
||||||
);
|
);
|
||||||
l_count := l_count + 1;
|
l_count := l_count + 1;
|
||||||
@@ -316,10 +338,14 @@ create or replace package body pck_net_storage as
|
|||||||
-- SCK 2026-04-08 Funktion erstellt
|
-- SCK 2026-04-08 Funktion erstellt
|
||||||
------------------------------------------------------------------------------------------------Kopf*/
|
------------------------------------------------------------------------------------------------Kopf*/
|
||||||
is
|
is
|
||||||
|
l_parent_folder varchar2(1024) := f_normalize_prefix(i_parent_folder);
|
||||||
begin
|
begin
|
||||||
pck_mitarbeiterrecht.p_hat_recht('LESEN_ALLES');
|
pck_mitarbeiterrecht.p_hat_recht('LESEN_ALLES');
|
||||||
p_assert_allowed(i_parent_folder);
|
if l_parent_folder is not null
|
||||||
return f_list_objects_internal(i_parent_folder, i_include_subfolders, i_start_with, i_limit);
|
then
|
||||||
|
p_assert_allowed(l_parent_folder);
|
||||||
|
end if;
|
||||||
|
return f_list_objects_internal(l_parent_folder, i_include_subfolders, i_start_with, i_limit);
|
||||||
end f_list_objects;
|
end f_list_objects;
|
||||||
|
|
||||||
function f_download_object (i_object_key in varchar2) return blob
|
function f_download_object (i_object_key in varchar2) return blob
|
||||||
@@ -384,6 +410,11 @@ create or replace package body pck_net_storage as
|
|||||||
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
||||||
p_assert_allowed(i_object_key);
|
p_assert_allowed(i_object_key);
|
||||||
|
|
||||||
|
if substr(i_object_key, -1) = '/'
|
||||||
|
then
|
||||||
|
raise_application_error(-20012, 'Object Key darf nicht mit / enden — zum Anlegen von Ordnern p_create_folder verwenden');
|
||||||
|
end if;
|
||||||
|
|
||||||
-- TEST
|
-- TEST
|
||||||
--l_response := f_make_request(
|
--l_response := f_make_request(
|
||||||
-- i_method => 'PUT'
|
-- i_method => 'PUT'
|
||||||
@@ -446,14 +477,14 @@ create or replace package body pck_net_storage as
|
|||||||
l_objects t_net_storage_tab;
|
l_objects t_net_storage_tab;
|
||||||
l_response clob;
|
l_response clob;
|
||||||
l_obj_path t_object_path;
|
l_obj_path t_object_path;
|
||||||
|
l_prefix varchar2(1024) := f_normalize_prefix(i_prefix);
|
||||||
begin
|
begin
|
||||||
pck_mitarbeiterrecht.p_hat_recht('ADMIN');
|
pck_mitarbeiterrecht.p_hat_recht('ADMIN');
|
||||||
p_assert_allowed(i_prefix);
|
p_assert_allowed(l_prefix);
|
||||||
|
|
||||||
-- TEST
|
|
||||||
-- Alle Objekte im Ordner auflisten (rekursiv, alle Tiefen)
|
-- Alle Objekte im Ordner auflisten (rekursiv, alle Tiefen)
|
||||||
--l_objects := f_list_objects_internal(
|
--l_objects := f_list_objects_internal(
|
||||||
-- i_parent_folder => i_prefix
|
-- i_parent_folder => l_prefix
|
||||||
-- ,i_include_subfolders => 'Y'
|
-- ,i_include_subfolders => 'Y'
|
||||||
-- ,i_start_with => null
|
-- ,i_start_with => null
|
||||||
-- ,i_limit => 0
|
-- ,i_limit => 0
|
||||||
@@ -471,12 +502,12 @@ create or replace package body pck_net_storage as
|
|||||||
-- end if;
|
-- end if;
|
||||||
--end loop;
|
--end loop;
|
||||||
|
|
||||||
l_obj_path := f_split_object_key(i_prefix);
|
l_obj_path := f_split_object_key(l_prefix);
|
||||||
pck_log.p_info(
|
pck_log.p_info(
|
||||||
i_module => c_log_module
|
i_module => c_log_module
|
||||||
,i_action => 'DELETE_FOLDER'
|
,i_action => 'DELETE_FOLDER'
|
||||||
,i_message => 'Ordner "' || l_obj_path.filename || '" rekursiv gelöscht | Pfad: ' || l_obj_path.path
|
,i_message => 'Ordner "' || l_obj_path.filename || '" rekursiv gelöscht | Pfad: ' || l_obj_path.path
|
||||||
,i_object_ref => i_prefix
|
,i_object_ref => l_prefix
|
||||||
);
|
);
|
||||||
end p_delete_folder;
|
end p_delete_folder;
|
||||||
|
|
||||||
@@ -504,6 +535,16 @@ create or replace package body pck_net_storage as
|
|||||||
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
||||||
p_assert_allowed(i_object_key);
|
p_assert_allowed(i_object_key);
|
||||||
|
|
||||||
|
if i_new_name is null or length(trim(i_new_name)) = 0
|
||||||
|
then
|
||||||
|
raise_application_error(-20013, 'Neuer Dateiname darf nicht leer sein');
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if instr(i_new_name, '/') > 0
|
||||||
|
then
|
||||||
|
raise_application_error(-20014, 'Dateiname darf keinen Schrägstrich enthalten — zum Verschieben p_move_object verwenden');
|
||||||
|
end if;
|
||||||
|
|
||||||
-- Verzeichnispfad aus dem aktuellen Key extrahieren
|
-- Verzeichnispfad aus dem aktuellen Key extrahieren
|
||||||
if instr(i_object_key, '/') > 0
|
if instr(i_object_key, '/') > 0
|
||||||
then
|
then
|
||||||
@@ -555,15 +596,24 @@ create or replace package body pck_net_storage as
|
|||||||
-- SCK 2026-04-08 Prozedur erstellt
|
-- SCK 2026-04-08 Prozedur erstellt
|
||||||
------------------------------------------------------------------------------------------------Kopf*/
|
------------------------------------------------------------------------------------------------Kopf*/
|
||||||
is
|
is
|
||||||
l_filename varchar2(1024);
|
l_filename varchar2(1024);
|
||||||
l_new_key varchar2(1024);
|
l_new_key varchar2(1024);
|
||||||
l_body clob;
|
l_body clob;
|
||||||
l_response clob;
|
l_response clob;
|
||||||
l_obj_path t_object_path;
|
l_obj_path t_object_path;
|
||||||
|
l_target_prefix varchar2(1024);
|
||||||
begin
|
begin
|
||||||
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
||||||
p_assert_allowed(i_object_key);
|
p_assert_allowed(i_object_key);
|
||||||
|
|
||||||
|
if i_target_prefix is null
|
||||||
|
then
|
||||||
|
raise_application_error(-20015, 'Zielpräfix darf nicht null sein');
|
||||||
|
end if;
|
||||||
|
|
||||||
|
l_target_prefix := f_normalize_prefix(i_target_prefix);
|
||||||
|
p_assert_allowed(l_target_prefix);
|
||||||
|
|
||||||
-- Dateinamen aus dem aktuellen Key extrahieren
|
-- Dateinamen aus dem aktuellen Key extrahieren
|
||||||
if instr(i_object_key, '/') > 0
|
if instr(i_object_key, '/') > 0
|
||||||
then
|
then
|
||||||
@@ -572,7 +622,7 @@ create or replace package body pck_net_storage as
|
|||||||
l_filename := i_object_key;
|
l_filename := i_object_key;
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
l_new_key := i_target_prefix || l_filename;
|
l_new_key := l_target_prefix || l_filename;
|
||||||
p_assert_allowed(l_new_key);
|
p_assert_allowed(l_new_key);
|
||||||
|
|
||||||
select json_object(
|
select json_object(
|
||||||
@@ -594,7 +644,7 @@ create or replace package body pck_net_storage as
|
|||||||
pck_log.p_info(
|
pck_log.p_info(
|
||||||
i_module => c_log_module
|
i_module => c_log_module
|
||||||
,i_action => 'MOVE'
|
,i_action => 'MOVE'
|
||||||
,i_message => 'Datei "' || l_obj_path.filename || '" verschoben | Von: ' || l_obj_path.path || ' | Nach: ' || i_target_prefix
|
,i_message => 'Datei "' || l_obj_path.filename || '" verschoben | Von: ' || l_obj_path.path || ' | Nach: ' || l_target_prefix
|
||||||
,i_object_ref => i_object_key
|
,i_object_ref => i_object_key
|
||||||
);
|
);
|
||||||
end p_move_object;
|
end p_move_object;
|
||||||
@@ -617,21 +667,35 @@ create or replace package body pck_net_storage as
|
|||||||
l_folder_key varchar2(1024);
|
l_folder_key varchar2(1024);
|
||||||
l_response clob;
|
l_response clob;
|
||||||
l_obj_path t_object_path;
|
l_obj_path t_object_path;
|
||||||
|
l_prefix varchar2(1024) := f_normalize_prefix(i_prefix);
|
||||||
begin
|
begin
|
||||||
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
pck_mitarbeiterrecht.p_hat_recht('SCHREIBEN_ALLES');
|
||||||
p_assert_allowed(i_prefix);
|
|
||||||
|
if i_folder_name is null or length(trim(i_folder_name)) = 0
|
||||||
|
then
|
||||||
|
raise_application_error(-20010, 'Ordnername darf nicht leer sein');
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if instr(i_folder_name, '/') > 0
|
||||||
|
then
|
||||||
|
raise_application_error(-20011, 'Ordnername darf keinen Schrägstrich enthalten');
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if l_prefix is not null
|
||||||
|
then
|
||||||
|
p_assert_allowed(l_prefix);
|
||||||
|
end if;
|
||||||
|
|
||||||
-- Ordner als leeres Objekt mit trailing Slash anlegen
|
-- Ordner als leeres Objekt mit trailing Slash anlegen
|
||||||
l_folder_key := i_prefix || i_folder_name || '/';
|
l_folder_key := l_prefix || i_folder_name || '/';
|
||||||
p_assert_allowed(l_folder_key);
|
p_assert_allowed(l_folder_key);
|
||||||
|
|
||||||
-- TEST
|
l_response := f_make_request(
|
||||||
--l_response := f_make_request(
|
i_method => 'PUT'
|
||||||
-- i_method => 'PUT'
|
,i_url => f_build_url(l_folder_key)
|
||||||
-- ,i_url => f_build_url(l_folder_key)
|
,i_body_blob => empty_blob()
|
||||||
-- ,i_body_blob => empty_blob()
|
,i_content_type => 'application/octet-stream'
|
||||||
-- ,i_content_type => 'application/octet-stream'
|
);
|
||||||
--);
|
|
||||||
|
|
||||||
l_obj_path := f_split_object_key(l_folder_key);
|
l_obj_path := f_split_object_key(l_folder_key);
|
||||||
pck_log.p_info(
|
pck_log.p_info(
|
||||||
|
|||||||
Reference in New Issue
Block a user