[GEMINI] Organize test suite to mirror new ctf.cli and ctf.forensics package structures
This commit is contained in:
204
tests/cli/test_forensics.py
Normal file
204
tests/cli/test_forensics.py
Normal file
@@ -0,0 +1,204 @@
|
||||
# tests/cli/test_forensics.py
|
||||
# {{{ imports
|
||||
import struct
|
||||
from pathlib import Path
|
||||
from click.testing import CliRunner
|
||||
from ctf.cli.forensics import forensics_group
|
||||
# }}}
|
||||
|
||||
# Define the persistent test environment path
|
||||
TEST_ENV = Path("tests/env")
|
||||
|
||||
# {{{ test_forensics_metadata_cli_success
|
||||
def test_forensics_metadata_cli_success():
|
||||
"""
|
||||
Verifies that 'forensics metadata' successfully runs via the CLI and prints
|
||||
the metadata in a formatted table.
|
||||
"""
|
||||
runner = CliRunner()
|
||||
test_file = TEST_ENV / "test_file.png"
|
||||
with open(test_file, "wb") as f:
|
||||
f.write(b"\x89PNG\r\n\x1a\n")
|
||||
|
||||
result = runner.invoke(forensics_group, ["metadata", str(test_file)])
|
||||
assert result.exit_code == 0
|
||||
assert "Metadata: test_file.png" in result.output
|
||||
assert "Size" in result.output
|
||||
assert "89504E47" in result.output
|
||||
assert "Detected Type" in result.output
|
||||
assert "PNG Image" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_nested_metadata_cli_success
|
||||
def test_nested_metadata_cli_success():
|
||||
"""
|
||||
Verifies that 'ctf forensics metadata' successfully runs via the root CLI.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
test_file = TEST_ENV / "test_file_nested.png"
|
||||
with open(test_file, "wb") as f:
|
||||
f.write(b"\x89PNG\r\n\x1a\n")
|
||||
|
||||
result = runner.invoke(cli, ["forensics", "metadata", str(test_file)])
|
||||
assert result.exit_code == 0
|
||||
assert "Metadata: test_file_nested.png" in result.output
|
||||
assert "PNG Image" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_forensics_metadata_cli_missing_file
|
||||
def test_forensics_metadata_cli_missing_file():
|
||||
"""
|
||||
Verifies that the CLI fails gracefully when a non-existent file path is specified.
|
||||
"""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(forensics_group, ["metadata", "non_existent_file.png"])
|
||||
assert result.exit_code != 0
|
||||
assert "does not exist" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_forensics_signatures_cli
|
||||
def test_forensics_signatures_cli():
|
||||
"""
|
||||
Verifies that 'forensics signatures' runs successfully and displays
|
||||
the list of supported file signatures.
|
||||
"""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(forensics_group, ["signatures"])
|
||||
assert result.exit_code == 0
|
||||
assert "Supported Magic Signatures" in result.output
|
||||
assert "PNG Image" in result.output
|
||||
assert "89504E47" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_exif_cli_options
|
||||
def test_exif_cli_options():
|
||||
"""
|
||||
Verifies CLI options -i/--inode and -e/--exif work as expected.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
test_file = TEST_ENV / "mock_exif_image.png"
|
||||
|
||||
# Verify mutual exclusion error
|
||||
result_err = runner.invoke(cli, ["forensics", "metadata", "-i", "-e", str(test_file)])
|
||||
assert result_err.exit_code != 0
|
||||
assert "Cannot specify more than one" in result_err.output
|
||||
|
||||
# Verify --inode hides EXIF data table
|
||||
result_inode = runner.invoke(cli, ["forensics", "metadata", "-i", str(test_file)])
|
||||
assert result_inode.exit_code == 0
|
||||
assert "Metadata: mock_exif_image.png" in result_inode.output
|
||||
assert "EXIF Metadata" not in result_inode.output
|
||||
|
||||
# Verify --exif hides POSIX table but displays EXIF data table
|
||||
result_exif = runner.invoke(cli, ["forensics", "metadata", "-e", str(test_file)])
|
||||
assert result_exif.exit_code == 0
|
||||
assert "Metadata: mock_exif_image.png" not in result_exif.output
|
||||
assert "EXIF Metadata" in result_exif.output
|
||||
assert "mock_exif_image" in result_exif.output
|
||||
assert "Make" in result_exif.output
|
||||
assert "TEST" in result_exif.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_exif_cli_options_warning
|
||||
def test_exif_cli_options_warning():
|
||||
"""
|
||||
Verifies CLI warns when no EXIF or comment is found and -e option is used.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
test_file = TEST_ENV / "clean_no_exif.png"
|
||||
with open(test_file, "wb") as f:
|
||||
f.write(b"\x89PNG\r\n\x1a\n")
|
||||
|
||||
result = runner.invoke(cli, ["forensics", "metadata", "-e", str(test_file)])
|
||||
assert result.exit_code == 0
|
||||
assert "No EXIF or comment metadata found" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_exif_cli_comment_only
|
||||
def test_exif_cli_comment_only():
|
||||
"""
|
||||
Verifies CLI displays comment when -e is used and only comment is found.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
comment_text = b"flag{comment_only}"
|
||||
comment_len = len(comment_text) + 2
|
||||
mock_jpeg = b"\xff\xd8\xff\xfe" + struct.pack(">H", comment_len) + comment_text
|
||||
|
||||
test_file = TEST_ENV / "comment_only.jpg"
|
||||
with open(test_file, "wb") as f:
|
||||
f.write(mock_jpeg)
|
||||
|
||||
result = runner.invoke(cli, ["forensics", "metadata", "-e", str(test_file)])
|
||||
assert result.exit_code == 0
|
||||
assert "Comment" in result.output
|
||||
assert "flag{comment_only}" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_adobe_xmp_cli
|
||||
def test_adobe_xmp_cli():
|
||||
"""
|
||||
Verifies that the ctf inspect CLI prints the parsed XMP fields.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
test_file = TEST_ENV / "mock_xmp.jpg"
|
||||
|
||||
result = runner.invoke(cli, ["forensics", "metadata", "-e", str(test_file)])
|
||||
assert result.exit_code == 0
|
||||
assert "EXIF Metadata" in result.output
|
||||
assert "cc:license" in result.output
|
||||
assert "cGljb0NURnt0ZXN0X3htcF9mbGFnfQ==" in result.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_cli_physical_option
|
||||
def test_cli_physical_option():
|
||||
"""
|
||||
Verifies the ctf forensics metadata CLI supports -p/--physical and mutual exclusion rules.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
test_file = TEST_ENV / "mock_physical.jpg"
|
||||
|
||||
# Mutual exclusion check
|
||||
result_err = runner.invoke(cli, ["forensics", "metadata", "-p", "-e", str(test_file)])
|
||||
assert result_err.exit_code != 0
|
||||
assert "Cannot specify more than one" in result_err.output
|
||||
|
||||
# Physical view check
|
||||
result_phys = runner.invoke(cli, ["forensics", "metadata", "-p", str(test_file)])
|
||||
assert result_phys.exit_code == 0
|
||||
assert "Physical Metadata" in result_phys.output
|
||||
assert "Image Size" in result_phys.output
|
||||
assert "1500x1000" in result_phys.output
|
||||
assert "Metadata: mock_physical.jpg" not in result_phys.output
|
||||
assert "EXIF Metadata" not in result_phys.output
|
||||
# }}}
|
||||
|
||||
# {{{ test_metadata_decoded_hints_cli
|
||||
def test_metadata_decoded_hints_cli():
|
||||
"""
|
||||
Verifies that metadata command successfully decodes and prints hex/base64 encoded metadata hints.
|
||||
"""
|
||||
from ctf.main import cli
|
||||
runner = CliRunner()
|
||||
|
||||
# Create a mock JPEG with a base64 encoded comment: "ZmxhZ3tiNjRfbWV0YWRhdGF9" -> "flag{b64_metadata}"
|
||||
comment_text = b"ZmxhZ3tiNjRfbWV0YWRhdGF9"
|
||||
comment_len = len(comment_text) + 2
|
||||
mock_jpeg = b"\xff\xd8\xff\xfe" + struct.pack(">H", comment_len) + comment_text + b"\xff\xd9"
|
||||
|
||||
test_file = TEST_ENV / "mock_encoded_comment.jpg"
|
||||
with open(test_file, "wb") as f:
|
||||
f.write(mock_jpeg)
|
||||
|
||||
result = runner.invoke(cli, ["forensics", "metadata", str(test_file)])
|
||||
assert result.exit_code == 0
|
||||
assert "Decoded Metadata Hints" in result.output
|
||||
assert "Comment" in result.output
|
||||
assert "base64" in result.output
|
||||
assert "flag{b64_metadata}" in result.output
|
||||
# }}}
|
||||
Reference in New Issue
Block a user