mirror of
https://github.com/Sea-Haven-Industries/procurement-ingest.git
synced 2026-10-07 16:08:54 +00:00
Reject --all combined with --prefix/--since in reprocess.py
--all is a distinct mode (the demoted full-prefix sweep), but the args.all branch unconditionally set prefix=inbound/ and since=None, so passing it alongside a narrower selector silently discarded that selector. `--all --since 2026-07-01` swept the entire corpus instead of the bounded window, triggering every documented --all hazard (Bedrock re-bill, metric double- count, merged-field regression) on objects the operator never targeted -- contradicting the tool's safety goal. Add the missing mutual-exclusion guard alongside the existing --key one, and pin --all+--prefix, --all+--since, and all three together as argparse rejections.
This commit is contained in:
parent
7765111781
commit
88805d906d
2 changed files with 58 additions and 0 deletions
|
|
@ -171,6 +171,13 @@ def main():
|
||||||
# --key is mutually exclusive with the listing selectors and with --all.
|
# --key is mutually exclusive with the listing selectors and with --all.
|
||||||
if args.key and (args.prefix or args.since or args.all):
|
if args.key and (args.prefix or args.since or args.all):
|
||||||
parser.error("--key cannot be combined with --prefix, --since, or --all")
|
parser.error("--key cannot be combined with --prefix, --since, or --all")
|
||||||
|
# --all is a distinct MODE (the demoted full-prefix sweep), not a modifier:
|
||||||
|
# reject it alongside the targeting selectors. Without this, the args.all
|
||||||
|
# clobber branch below would silently discard a narrower --prefix/--since
|
||||||
|
# (e.g. `--all --since 2026-07-01` would sweep the ENTIRE corpus, not the
|
||||||
|
# bounded window) and trigger every --all hazard on unintended objects.
|
||||||
|
if args.all and (args.prefix or args.since):
|
||||||
|
parser.error("--all cannot be combined with --prefix or --since")
|
||||||
|
|
||||||
account_id = boto3.client("sts").get_caller_identity()["Account"]
|
account_id = boto3.client("sts").get_caller_identity()["Account"]
|
||||||
function_name = PIPELINES[args.pipeline]["function"]
|
function_name = PIPELINES[args.pipeline]["function"]
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ replay single-decode-correct if a future handler ever adds unquote_plus.
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
# reprocess.py builds its boto3 clients inside main()/functions, so importing
|
# reprocess.py builds its boto3 clients inside main()/functions, so importing
|
||||||
# it is side-effect-free (no AWS calls, no clients at module scope). Set dummy
|
# it is side-effect-free (no AWS calls, no clients at module scope). Set dummy
|
||||||
|
|
@ -71,3 +74,51 @@ def test_build_s3_event_shape_and_raw_key():
|
||||||
assert "+" in emitted
|
assert "+" in emitted
|
||||||
# '%41' NOT decoded to 'A':
|
# '%41' NOT decoded to 'A':
|
||||||
assert "%41" in emitted and "A .eml" not in emitted
|
assert "%41" in emitted and "A .eml" not in emitted
|
||||||
|
|
||||||
|
|
||||||
|
# --- Selector mutual-exclusion: --all is a MODE, not a modifier ---------------
|
||||||
|
# reprocess.main() reaches its first AWS call (sts get_caller_identity) only
|
||||||
|
# AFTER the argparse mutual-exclusion gate, so an invalid selector combination
|
||||||
|
# raises SystemExit(2) with no AWS access needed. This pins that `--all` cannot
|
||||||
|
# be silently combined with a narrower --prefix/--since (which the args.all
|
||||||
|
# clobber branch would otherwise discard -> unintended whole-corpus sweep).
|
||||||
|
def _expect_argparse_reject(monkeypatch, capsys, argv):
|
||||||
|
monkeypatch.setattr(sys, "argv", ["reprocess.py", *argv])
|
||||||
|
with pytest.raises(SystemExit) as exc:
|
||||||
|
reprocess.main()
|
||||||
|
assert exc.value.code == 2 # argparse error exit code
|
||||||
|
return capsys.readouterr().err
|
||||||
|
|
||||||
|
|
||||||
|
def test_all_rejects_prefix(monkeypatch, capsys):
|
||||||
|
err = _expect_argparse_reject(
|
||||||
|
monkeypatch,
|
||||||
|
capsys,
|
||||||
|
["--pipeline", "po", "--all", "--prefix", "inbound/2026/07/"],
|
||||||
|
)
|
||||||
|
assert "--all cannot be combined with --prefix or --since" in err
|
||||||
|
|
||||||
|
|
||||||
|
def test_all_rejects_since(monkeypatch, capsys):
|
||||||
|
err = _expect_argparse_reject(
|
||||||
|
monkeypatch,
|
||||||
|
capsys,
|
||||||
|
["--pipeline", "po", "--all", "--since", "2026-07-01T00:00:00Z"],
|
||||||
|
)
|
||||||
|
assert "--all cannot be combined with --prefix or --since" in err
|
||||||
|
|
||||||
|
|
||||||
|
def test_all_rejects_prefix_and_since_together(monkeypatch, capsys):
|
||||||
|
_expect_argparse_reject(
|
||||||
|
monkeypatch,
|
||||||
|
capsys,
|
||||||
|
[
|
||||||
|
"--pipeline",
|
||||||
|
"po",
|
||||||
|
"--all",
|
||||||
|
"--prefix",
|
||||||
|
"inbound/2026/",
|
||||||
|
"--since",
|
||||||
|
"2026-07-01T00:00:00Z",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue