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)
|
# Multi-target testing (source code + deployed app)
|
||||||
strix -t https://github.com/org/app -t https://your-app.com
|
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
|
strix --target-list ./targets.txt
|
||||||
|
|
||||||
# White-box source-aware scan (local repository)
|
# White-box source-aware scan (local repository)
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ strix --target https://your-app.com
|
|||||||
# Multiple targets (white-box testing)
|
# Multiple targets (white-box testing)
|
||||||
strix -t https://github.com/org/repo -t https://your-app.com
|
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
|
strix --target-list ./targets.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ strix (--target <target> | --target-list <path> | --mount <path>) [options]
|
|||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="--target-list" type="string">
|
<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>
|
||||||
|
|
||||||
<ParamField path="--mount" type="string">
|
<ParamField path="--mount" type="string">
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ Examples:
|
|||||||
strix --target https://github.com/user/repo --target https://example.com
|
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
|
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
|
strix --target-list ./targets.txt
|
||||||
|
|
||||||
# Custom instructions (inline)
|
# Custom instructions (inline)
|
||||||
@@ -378,8 +378,8 @@ Examples:
|
|||||||
type=str,
|
type=str,
|
||||||
action="append",
|
action="append",
|
||||||
metavar="PATH",
|
metavar="PATH",
|
||||||
help="Path to a file containing targets, one per non-empty line. Can be specified "
|
help="Path to a file containing targets, one per non-empty, non-comment line. "
|
||||||
"multiple times and combined with --target.",
|
"Can be specified multiple times and combined with --target.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--mount",
|
"--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]:
|
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():
|
if not path_str or not path_str.strip():
|
||||||
raise ValueError("--target-list path must not be empty.")
|
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.")
|
raise ValueError(f"Target list file '{path_str}' is not an existing file.")
|
||||||
|
|
||||||
try:
|
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:
|
except OSError as e:
|
||||||
raise ValueError(f"Failed to read target list file '{path_str}': {e!s}") from 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:
|
def test_read_target_list_file_rejects_empty_file(tmp_path: Path) -> None:
|
||||||
target_list = tmp_path / "targets.txt"
|
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"):
|
with pytest.raises(ValueError, match="is empty"):
|
||||||
read_target_list_file(str(target_list))
|
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"))
|
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", ["", " "])
|
@pytest.mark.parametrize("empty", ["", " "])
|
||||||
def test_read_target_list_file_rejects_empty_path(empty: str) -> None:
|
def test_read_target_list_file_rejects_empty_path(empty: str) -> None:
|
||||||
with pytest.raises(ValueError, match="must not be empty"):
|
with pytest.raises(ValueError, match="must not be empty"):
|
||||||
|
|||||||
Reference in New Issue
Block a user