Handle target list comments and encoding errors
This commit is contained in:
@@ -169,7 +169,7 @@ strix --target https://your-app.com --instruction "Perform authenticated testing
|
||||
# Multi-target testing (source code + deployed app)
|
||||
strix -t https://github.com/org/app -t https://your-app.com
|
||||
|
||||
# Targets from a file, one target per non-empty line
|
||||
# Targets from a file, one target per non-empty, non-comment line
|
||||
strix --target-list ./targets.txt
|
||||
|
||||
# White-box source-aware scan (local repository)
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ strix --target https://your-app.com
|
||||
# Multiple targets (white-box testing)
|
||||
strix -t https://github.com/org/repo -t https://your-app.com
|
||||
|
||||
# Targets from a file, one target per non-empty line
|
||||
# Targets from a file, one target per non-empty, non-comment line
|
||||
strix --target-list ./targets.txt
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ strix (--target <target> | --target-list <path> | --mount <path>) [options]
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--target-list" type="string">
|
||||
Path to a file containing targets, one per non-empty line. Can be specified multiple times and combined with `--target`.
|
||||
Path to a file containing targets, one per non-empty, non-comment line. Lines starting with `#` are ignored. Can be specified multiple times and combined with `--target`.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--mount" type="string">
|
||||
|
||||
@@ -345,7 +345,7 @@ Examples:
|
||||
strix --target https://github.com/user/repo --target https://example.com
|
||||
strix --target ./my-project --target https://staging.example.com --target https://prod.example.com
|
||||
|
||||
# Targets from a file, one target per line
|
||||
# Targets from a file, one target per non-empty, non-comment line
|
||||
strix --target-list ./targets.txt
|
||||
|
||||
# Custom instructions (inline)
|
||||
@@ -378,8 +378,8 @@ Examples:
|
||||
type=str,
|
||||
action="append",
|
||||
metavar="PATH",
|
||||
help="Path to a file containing targets, one per non-empty line. Can be specified "
|
||||
"multiple times and combined with --target.",
|
||||
help="Path to a file containing targets, one per non-empty, non-comment line. "
|
||||
"Can be specified multiple times and combined with --target.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--mount",
|
||||
|
||||
@@ -1132,7 +1132,7 @@ def infer_target_type(target: str) -> tuple[str, dict[str, str]]: # noqa: PLR09
|
||||
|
||||
|
||||
def read_target_list_file(path_str: str) -> list[str]:
|
||||
"""Read scan targets from a file, one target per non-empty line."""
|
||||
"""Read scan targets from a file, one target per non-empty, non-comment line."""
|
||||
if not path_str or not path_str.strip():
|
||||
raise ValueError("--target-list path must not be empty.")
|
||||
|
||||
@@ -1141,7 +1141,15 @@ def read_target_list_file(path_str: str) -> list[str]:
|
||||
raise ValueError(f"Target list file '{path_str}' is not an existing file.")
|
||||
|
||||
try:
|
||||
targets = [line.strip() for line in path.read_text(encoding="utf-8").splitlines()]
|
||||
targets = [
|
||||
target
|
||||
for line in path.read_text(encoding="utf-8").splitlines()
|
||||
if (target := line.strip()) and not target.startswith("#")
|
||||
]
|
||||
except UnicodeDecodeError as e:
|
||||
raise ValueError(
|
||||
f"Target list file '{path_str}' must be valid UTF-8 text: {e!s}"
|
||||
) from e
|
||||
except OSError as e:
|
||||
raise ValueError(f"Failed to read target list file '{path_str}': {e!s}") from e
|
||||
|
||||
|
||||
@@ -175,9 +175,25 @@ def test_read_target_list_file_strips_blank_lines(tmp_path: Path) -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_read_target_list_file_ignores_comment_lines(tmp_path: Path) -> None:
|
||||
target_list = tmp_path / "targets.txt"
|
||||
target_list.write_text(
|
||||
"# production targets\n"
|
||||
"https://test1.com/\n"
|
||||
" # staging targets\n"
|
||||
"http://test2.com:5789/\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
assert read_target_list_file(str(target_list)) == [
|
||||
"https://test1.com/",
|
||||
"http://test2.com:5789/",
|
||||
]
|
||||
|
||||
|
||||
def test_read_target_list_file_rejects_empty_file(tmp_path: Path) -> None:
|
||||
target_list = tmp_path / "targets.txt"
|
||||
target_list.write_text(" \n\n", encoding="utf-8")
|
||||
target_list.write_text(" \n# no targets yet\n\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match="is empty"):
|
||||
read_target_list_file(str(target_list))
|
||||
@@ -188,6 +204,14 @@ def test_read_target_list_file_rejects_missing_path(tmp_path: Path) -> None:
|
||||
read_target_list_file(str(tmp_path / "missing.txt"))
|
||||
|
||||
|
||||
def test_read_target_list_file_rejects_non_utf8_file(tmp_path: Path) -> None:
|
||||
target_list = tmp_path / "targets.txt"
|
||||
target_list.write_bytes(b"https://test1.com/\xff\n")
|
||||
|
||||
with pytest.raises(ValueError, match="must be valid UTF-8 text"):
|
||||
read_target_list_file(str(target_list))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("empty", ["", " "])
|
||||
def test_read_target_list_file_rejects_empty_path(empty: str) -> None:
|
||||
with pytest.raises(ValueError, match="must not be empty"):
|
||||
|
||||
Reference in New Issue
Block a user