prevent job path leak

This commit is contained in:
henryruhs
2026-09-12 18:50:20 +02:00
parent 7bf5434997
commit 6abc7d58cb
14 changed files with 148 additions and 38 deletions
+4
View File
@@ -9,6 +9,7 @@ from facefusion.apis import asset_store
from facefusion.apis.session_helper import validate_api_key
from facefusion.apis.stream_manager import destroy_stream
from facefusion.filesystem import is_directory, remove_directory
from facefusion.jobs import job_manager
async def create_session(request : Request) -> JSONResponse:
@@ -29,6 +30,9 @@ async def create_session(request : Request) -> JSONResponse:
process_manager.init()
rtc_store.init()
jobs_path = state_manager.get_jobs_path()
job_manager.init_jobs(jobs_path)
return JSONResponse(
{
'access_token': session.get('access_token'),
+15 -16
View File
@@ -3,6 +3,7 @@ from copy import copy
from typing import List, Optional
import facefusion.choices
from facefusion import state_manager
from facefusion.filesystem import create_directory, get_file_name, is_directory, is_file, move_file, remove_directory, remove_file, resolve_file_pattern
from facefusion.jobs.job_helper import get_step_output_path
from facefusion.json import read_json, write_json
@@ -10,14 +11,9 @@ from facefusion.sanitizer import sanitize_job_id
from facefusion.time_helper import get_current_date_time
from facefusion.types import Args, Job, JobSet, JobStatus, JobStep, JobStepStatus
JOBS_PATH : Optional[str] = None
def init_jobs(jobs_path : str) -> bool:
global JOBS_PATH
JOBS_PATH = jobs_path
job_status_paths = [ os.path.join(JOBS_PATH, job_status) for job_status in facefusion.choices.job_statuses ]
job_status_paths = [ os.path.join(jobs_path, job_status) for job_status in facefusion.choices.job_statuses ]
for job_status_path in job_status_paths:
create_directory(job_status_path)
@@ -91,13 +87,14 @@ def find_jobs(job_status : JobStatus) -> JobSet:
def find_job_ids(job_status : JobStatus) -> List[str]:
job_pattern = os.path.join(JOBS_PATH, job_status, '*.json')
job_paths = resolve_file_pattern(job_pattern)
job_paths.sort(key = os.path.getmtime)
jobs_path = state_manager.get_jobs_path()
job_file_pattern = os.path.join(jobs_path, job_status, '*.json')
job_file_paths = resolve_file_pattern(job_file_pattern)
job_file_paths.sort(key = os.path.getmtime)
job_ids = []
for job_path in job_paths:
job_id = get_file_name(job_path)
for job_file_path in job_file_paths:
job_id = get_file_name(job_file_path)
job_ids.append(job_id)
return job_ids
@@ -240,23 +237,25 @@ def delete_job_file(job_id : str) -> bool:
def suggest_job_path(job_id : str, job_status : JobStatus) -> Optional[str]:
jobs_path = state_manager.get_jobs_path()
job_file_name = get_job_file_name(job_id)
if job_file_name:
return os.path.join(JOBS_PATH, job_status, job_file_name)
return os.path.join(jobs_path, job_status, job_file_name)
return None
def find_job_path(job_id : str) -> Optional[str]:
jobs_path = state_manager.get_jobs_path()
job_file_name = get_job_file_name(job_id)
if job_file_name:
for job_status in facefusion.choices.job_statuses:
job_pattern = os.path.join(JOBS_PATH, job_status, job_file_name)
job_paths = resolve_file_pattern(job_pattern)
job_file_pattern = os.path.join(jobs_path, job_status, job_file_name)
job_file_paths = resolve_file_pattern(job_file_pattern)
for job_path in job_paths:
return job_path
for job_file_path in job_file_paths:
return job_file_path
return None
+2 -1
View File
@@ -9,7 +9,7 @@ from facefusion import ffmpeg, ffmpeg_builder, metadata, process_manager, sessio
from facefusion.apis import asset_store
from facefusion.apis.core import create_api
from facefusion.download import conditional_download
from .assert_helper import get_test_example_file, get_test_examples_directory
from .assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
@@ -42,6 +42,7 @@ def before_each() -> Iterator[None]:
session_context.set_session_id(local_id)
state_manager.init_item('temp_path', tempfile.gettempdir())
state_manager.init_item('jobs_path', get_test_jobs_directory())
state_manager.init_item('temp_frame_format', 'png')
session_manager.SESSIONS.clear()
asset_store.delete_assets()
+60 -1
View File
@@ -37,8 +37,9 @@ def before_each() -> Iterator[None]:
state_manager.init_item('source_paths', [ get_test_example_file('source.jpg') ])
state_manager.init_item('target_path', get_test_example_file('target-240p.mp4'))
state_manager.init_item('temp_path', get_test_jobs_directory())
state_manager.init_item('jobs_path', get_test_jobs_directory())
clear_jobs(get_test_jobs_directory())
init_jobs(get_test_jobs_directory())
init_jobs(state_manager.get_jobs_path())
yield
@@ -62,6 +63,8 @@ def test_get_jobs(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
get_jobs_response = test_client.get('/jobs?status=invalid', headers =
{
@@ -83,6 +86,22 @@ def test_get_jobs(test_client : TestClient) -> None:
assert 'job-test-get-jobs' in get_jobs_body
assert get_jobs_response.status_code == 200
create_session_response = test_client.post('/session', json =
{
'client_version': metadata.get('version')
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
get_jobs_response = test_client.get('/jobs?status=drafted', headers =
{
'Authorization': 'Bearer ' + access_token
})
get_jobs_body = get_jobs_response.json()
assert get_jobs_body == {}
assert get_jobs_response.status_code == 200
def test_get_job(test_client : TestClient) -> None:
get_job_response = test_client.get('/jobs/job-test-get-job')
@@ -95,6 +114,8 @@ def test_get_job(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
get_job_response = test_client.get('/jobs/job-test-unknown', headers =
{
@@ -116,6 +137,22 @@ def test_get_job(test_client : TestClient) -> None:
assert get_job_body.get('version') == '1'
assert get_job_response.status_code == 200
create_session_response = test_client.post('/session', json =
{
'client_version': metadata.get('version')
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
get_job_response = test_client.get('/jobs/job-test-get-job', headers =
{
'Authorization': 'Bearer ' + access_token
})
get_job_body = get_job_response.json()
assert get_job_body.get('message') == 'job not found'
assert get_job_response.status_code == 404
def test_create_job(test_client : TestClient) -> None:
create_job_response = test_client.post('/jobs')
@@ -128,6 +165,8 @@ def test_create_job(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
create_job_response = test_client.post('/jobs', headers =
{
@@ -167,6 +206,8 @@ def test_submit_jobs(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
submit_jobs_response = test_client.patch('/jobs?action=invalid', headers =
{
@@ -218,6 +259,8 @@ def test_submit_job(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
submit_job_response = test_client.patch('/jobs/job-test-submit-job?action=invalid', headers =
{
@@ -269,6 +312,8 @@ def test_run_jobs(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
run_jobs_response = test_client.patch('/jobs?action=run', headers =
{
@@ -315,6 +360,8 @@ def test_run_job(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
create_job('job-test-run-job')
@@ -362,6 +409,8 @@ def test_retry_jobs(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
retry_jobs_response = test_client.patch('/jobs?action=retry', headers =
{
@@ -410,6 +459,8 @@ def test_retry_job(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
retry_job_response = test_client.patch('/jobs/job-test-retry-job?action=retry', headers =
{
@@ -458,6 +509,8 @@ def test_delete_jobs(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
delete_jobs_response = test_client.delete('/jobs', headers =
{
@@ -491,6 +544,8 @@ def test_delete_job(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
delete_job_response = test_client.delete('/jobs/job-test-unknown', headers =
{
@@ -526,6 +581,8 @@ def test_create_step(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
create_job('job-test-create-step')
@@ -606,6 +663,8 @@ def test_delete_step(test_client : TestClient) -> None:
})
create_session_body = create_session_response.json()
access_token = create_session_body.get('access_token')
session_id = session_manager.find_session_id(access_token)
session_context.set_session_id(session_id)
create_job('job-test-delete-step')
+8 -1
View File
@@ -4,8 +4,15 @@ import pytest
from pytest_mock import MockerFixture
from starlette.testclient import TestClient
from facefusion import metadata, session_manager
from facefusion import metadata, session_manager, state_manager
from facefusion.apis.core import create_api
from .assert_helper import get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
@pytest.fixture(scope = 'function', autouse = True)
+8 -1
View File
@@ -3,8 +3,15 @@ from typing import Iterator
import pytest
from starlette.testclient import TestClient
from facefusion import metadata, session_manager
from facefusion import metadata, session_manager, state_manager
from facefusion.apis.core import create_api
from .assert_helper import get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
@pytest.fixture(scope = 'function', autouse = True)
+2 -1
View File
@@ -14,7 +14,7 @@ from facefusion.apis.core import create_api
from facefusion.download import conditional_download
from facefusion.libraries import datachannel as datachannel_module
from facefusion.types import RtcPeer, Session
from .assert_helper import get_test_example_file, get_test_examples_directory
from .assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
@@ -36,6 +36,7 @@ def before_each() -> Iterator[None]:
session_context.set_session_id(local_id)
state_manager.init_item('temp_path', tempfile.gettempdir())
state_manager.init_item('jobs_path', get_test_jobs_directory())
session_manager.SESSIONS.clear()
asset_store.delete_assets()
+2 -1
View File
@@ -8,12 +8,13 @@ from facefusion import capability_store, ffmpeg, ffmpeg_builder, metadata, proce
from facefusion.apis import asset_store
from facefusion.apis.core import create_api
from facefusion.download import conditional_download
from .assert_helper import get_test_example_file, get_test_examples_directory
from .assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
process_manager.start()
program = ArgumentParser()
+2 -1
View File
@@ -12,7 +12,7 @@ from facefusion.download import conditional_download
from facefusion.hash_helper import create_hash
from facefusion.libraries import datachannel as datachannel_module
from facefusion.types import VideoCodec
from .assert_helper import get_test_example_file, get_test_examples_directory
from .assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
@@ -22,6 +22,7 @@ def before_all() -> None:
state_manager.init_item('execution_providers', [ 'cpu' ])
state_manager.init_item('download_providers', [ 'github', 'huggingface' ])
state_manager.init_item('temp_path', tempfile.gettempdir())
state_manager.init_item('jobs_path', get_test_jobs_directory())
state_manager.init_item('processors', [])
pre_check()
+2 -6
View File
@@ -1,4 +1,3 @@
import os
import subprocess
import sys
@@ -7,13 +6,13 @@ import pytest
from facefusion import ffmpeg, ffmpeg_builder, process_manager, state_manager
from facefusion.download import conditional_download
from facefusion.jobs.job_manager import clear_jobs, count_step_total, init_jobs
from facefusion.session_context import resolve_local_id
from .assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_path, is_test_job_file
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
process_manager.start()
conditional_download(get_test_examples_directory(),
@@ -36,11 +35,8 @@ def before_all() -> None:
@pytest.fixture(scope = 'function', autouse = True)
def before_each() -> None:
local_id = resolve_local_id()
jobs_path = os.path.join(get_test_jobs_directory(), local_id)
clear_jobs(get_test_jobs_directory())
init_jobs(jobs_path)
init_jobs(state_manager.get_jobs_path())
def test_job_list() -> None:
+2 -6
View File
@@ -1,4 +1,3 @@
import os
import subprocess
import sys
@@ -7,13 +6,13 @@ import pytest
from facefusion import ffmpeg, ffmpeg_builder, process_manager, state_manager
from facefusion.download import conditional_download
from facefusion.jobs.job_manager import clear_jobs, init_jobs, move_job_file, set_steps_status
from facefusion.session_context import resolve_local_id
from .assert_helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_path, is_test_output_file, prepare_test_output_directory
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
process_manager.start()
conditional_download(get_test_examples_directory(),
@@ -36,11 +35,8 @@ def before_all() -> None:
@pytest.fixture(scope = 'function', autouse = True)
def before_each() -> None:
local_id = resolve_local_id()
jobs_path = os.path.join(get_test_jobs_directory(), local_id)
clear_jobs(get_test_jobs_directory())
init_jobs(jobs_path)
init_jobs(state_manager.get_jobs_path())
prepare_test_output_directory()
+8 -1
View File
@@ -2,15 +2,22 @@ from time import sleep
import pytest
from facefusion import state_manager
from facefusion.jobs.job_list import compose_job_list
from facefusion.jobs.job_manager import clear_jobs, create_job, init_jobs
from .assert_helper import get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
@pytest.fixture(scope = 'function', autouse = True)
def before_each() -> None:
clear_jobs(get_test_jobs_directory())
init_jobs(get_test_jobs_directory())
init_jobs(state_manager.get_jobs_path())
def test_compose_job_list() -> None:
+31 -1
View File
@@ -1,16 +1,46 @@
import os
from time import sleep
import pytest
from facefusion import state_manager
from facefusion.jobs.job_helper import get_step_output_path
from facefusion.jobs.job_manager import add_step, clear_jobs, count_step_total, create_job, delete_job, delete_jobs, find_job_ids, find_jobs, get_steps, init_jobs, insert_step, move_job_file, remix_step, remove_step, set_step_status, set_steps_status, submit_job, submit_jobs
from facefusion.session_context import resolve_local_id, set_session_id
from .assert_helper import get_test_jobs_directory
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
@pytest.fixture(scope = 'function', autouse = True)
def before_each() -> None:
local_id = resolve_local_id()
set_session_id(local_id)
clear_jobs(get_test_jobs_directory())
init_jobs(get_test_jobs_directory())
init_jobs(state_manager.get_jobs_path())
def test_init_jobs() -> None:
local_id = resolve_local_id()
set_session_id('session-a')
state_manager.init()
assert init_jobs(state_manager.get_jobs_path()) is True
assert os.path.isdir(os.path.join(get_test_jobs_directory(), 'session-a', 'drafted')) is True
create_job('job-test-init-jobs')
assert find_job_ids('drafted') == [ 'job-test-init-jobs' ]
set_session_id(local_id)
assert find_job_ids('drafted') == []
def test_create_job() -> None:
+2 -1
View File
@@ -14,6 +14,7 @@ from .assert_helper import get_test_example_file, get_test_examples_directory, g
@pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None:
state_manager.init()
state_manager.init_item('jobs_path', get_test_jobs_directory())
process_manager.start()
conditional_download(get_test_examples_directory(),
@@ -37,7 +38,7 @@ def before_all() -> None:
@pytest.fixture(scope = 'function', autouse = True)
def before_each() -> None:
clear_jobs(get_test_jobs_directory())
init_jobs(get_test_jobs_directory())
init_jobs(state_manager.get_jobs_path())
prepare_test_output_directory()