DedSec Project

Async cancellation, safe paths and subprocess execution — DedSec Project

Async cancellation, safe paths and subprocess execution Async IO and cancellation Track: Systems Core explanation Asynchronous IO avoids blocking while waiting on external operations. It…

Async IO and cancellation

Track: Systems

Core explanation

Asynchronous IO avoids blocking while waiting on external operations. It does not make CPU-heavy tasks automatically faster. Support timeouts, cancellation and resource cleanup.

Hands-on application

Design an asynchronous request timeout with cleanup in finally.

How the system actually behaves

Asynchronous I/O interleaves tasks while waiting for operations. async def returns a coroutine object that must be awaited or scheduled. Cancellation is part of the task's lifecycle: cleanup belongs in finally, and swallowing CancelledError can prevent graceful shutdown.

Runnable controlled experiment

import asyncio
async def worker():
    try: await asyncio.sleep(10)
    finally: print('cleanup')
async def main():
    task=asyncio.create_task(worker())
    await asyncio.sleep(0)
    task.cancel()
    try: await task
    except asyncio.CancelledError: pass
asyncio.run(main())

Failure analysis and extension

Record the order of creation, first suspension, cancellation and cleanup. Compare cancelling a Task with merely abandoning a coroutine; add a shared resource and verify finally closes it.

Reference: https://docs.python.org/3/library/asyncio-task.html

Filesystem paths and symlinks

Track: Systems

Core explanation

Canonicalization, relative paths and symbolic links affect access control. For archive extraction and uploads, reject paths escaping the intended root; a string prefix alone is not a secure containment test.

Hands-on application

Check whether a resolved child path remains inside a temporary workspace.

How the system actually behaves

A path is a name resolved by the filesystem, not a permanent identity of the target object. Symlinks, relative components and mount points can alter the resolved destination. Path.resolve is useful for diagnostics but does not alone prevent a time-of-check/time-of-use race if another process can change the tree.

Runnable controlled experiment

from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as p:
    root=Path(p).resolve()
    (root/'ok.txt').write_text('ok')
    assert (root/'ok.txt').resolve().is_relative_to(root)

Failure analysis and extension

Compare an inside file and a sibling named root-extra to show why string startswith is unsafe. For hostile filesystem writers, research directory-relative, no-follow file opens and validate the opened descriptor.

Reference: https://docs.python.org/3/library/pathlib.html

Subprocess safety

Track: Systems

Core explanation

Pass argument arrays to subprocess APIs and avoid shell interpretation for untrusted values. Use timeouts, explicit working directories and checked return codes.

Hands-on application

Invoke a benign version-check command with an argument list, not string interpolation.

How the system actually behaves

Subprocess arguments are data when passed as an explicit array with shell=False; joining user strings into a shell command changes the parsing and may execute unintended operators. Limit runtime, check exit codes and capture only bounded output when external commands handle untrusted inputs.

Runnable controlled experiment

import subprocess,sys
text='hello; echo unexpected'
p=subprocess.run([sys.executable,'-c','import sys; print(sys.argv[1])',text],capture_output=True,text=True,check=True,timeout=3)
assert p.stdout.strip()==text

Failure analysis and extension

Test a command returning exit code 7 and catch CalledProcessError. Test a child exceeding its timeout; compare FileNotFoundError with a nonzero exit and reject arbitrary executable paths by policy.

Reference: https://docs.python.org/3/library/subprocess.html