sqlmap/tests/test_purge.py
Miroslav Štampar cb20a446ae
Some checks are pending
/ build (macos-latest, 3.8) (push) Waiting to run
/ build (ubuntu-latest, pypy-2.7) (push) Waiting to run
/ build (windows-latest, 3.14) (push) Waiting to run
Update of unit tests
2026-06-28 14:28:42 +02:00

124 lines
4.4 KiB
Python

#!/usr/bin/env python
"""
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
Secure directory purge (lib/utils/purge.py, the --purge feature): multi-pass
overwrite + truncation + removal of a directory's content. Driven against a
throwaway temp tree so the real output dir is never touched.
"""
import os
import shutil
import sys
import tempfile
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap
bootstrap()
import lib.utils.purge as purge_mod
from lib.utils.purge import purge
class _RecordingLogger(object):
"""Captures every (level, message) emitted while installed as purge.logger."""
def __init__(self):
self.records = []
def _add(self, level):
return lambda msg, *a: self.records.append((level, msg % a if a else msg))
def __getattr__(self, name):
if name in ("warning", "info", "debug", "error", "critical"):
return self._add(name)
raise AttributeError(name)
def messages(self, level=None):
return [m for (lvl, m) in self.records if level is None or lvl == level]
class TestPurge(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.mkdtemp(prefix="sqlmap_purge_")
def tearDown(self):
shutil.rmtree(self.tmp, ignore_errors=True)
def test_overwrites_and_truncates_file_contents(self):
# a couple of files + a nested subdir with a (non-empty) file
plaintexts = {
os.path.join(self.tmp, "a.txt"): "secret data",
os.path.join(self.tmp, "sub", "b.txt"): "more secret data",
}
with open(os.path.join(self.tmp, "a.txt"), "w") as f:
f.write(plaintexts[os.path.join(self.tmp, "a.txt")])
with open(os.path.join(self.tmp, "empty.bin"), "w") as f:
pass
os.mkdir(os.path.join(self.tmp, "sub"))
with open(os.path.join(self.tmp, "sub", "b.txt"), "w") as f:
f.write(plaintexts[os.path.join(self.tmp, "sub", "b.txt")])
# neutralise the final rmtree so the overwrite/truncate work product remains
# observable on disk; the files are renamed, so locate them by walking the tree.
real_rmtree = purge_mod.shutil.rmtree
purge_mod.shutil.rmtree = lambda *a, **k: None
try:
purge(self.tmp)
finally:
purge_mod.shutil.rmtree = real_rmtree
# collect every surviving regular file (names are randomised by purge)
survivors = []
for root, _dirs, files in os.walk(self.tmp):
for name in files:
survivors.append(os.path.join(root, name))
# the originally non-empty files still exist (rmtree was a no-op) but the
# multi-pass overwrite + truncation reduced each to size 0 and the original
# plaintext is gone.
nonempty = [p for p in survivors if os.path.getsize(p) > 0]
self.assertEqual(nonempty, [], msg="files were not truncated to zero: %r" % nonempty)
blob = b"".join(open(p, "rb").read() for p in survivors)
for secret in plaintexts.values():
self.assertNotIn(secret.encode("utf-8"), blob,
msg="original plaintext %r survived the purge" % secret)
def test_purges_nested_content(self):
# full purge (including rmtree) wipes the whole tree
with open(os.path.join(self.tmp, "a.txt"), "w") as f:
f.write("secret data")
sub = os.path.join(self.tmp, "sub")
os.mkdir(sub)
with open(os.path.join(sub, "b.txt"), "w") as f:
f.write("more secret data")
purge(self.tmp)
self.assertFalse(os.path.exists(self.tmp))
def test_nonexistent_directory_is_noop(self):
missing = os.path.join(self.tmp, "does_not_exist")
real_logger = purge_mod.logger
rec = _RecordingLogger()
purge_mod.logger = rec
try:
# must not raise; the guard branch logs a skip warning and returns
purge(missing)
finally:
purge_mod.logger = real_logger
self.assertFalse(os.path.exists(missing))
self.assertTrue(
any("skipping purging" in w and "does not exist" in w for w in rec.messages("warning")),
msg="nonexistent-directory guard did not log its warning: %r" % rec.records,
)
if __name__ == "__main__":
unittest.main()