#!/usr/bin/env python3 """Validate and normalize Phase 0 input for the JS reverse automation skill. Usage: python3 scripts/check_inputs.py --input --output artifacts/phase0_input.json python3 scripts/check_inputs.py --analysis analysis_result.json --schema schemas/analysis_result.schema.json --output artifacts/input_validation.json """ from __future__ import annotations import argparse import json from pathlib import Path from urllib.parse import urlparse from common import dump_json, load_json def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument("--input", help="Path to raw input JSON (Phase 0 mode).") parser.add_argument("--analysis", help="Path to analysis_result.json (schema validation mode).") parser.add_argument("--schema", default="", help="Path to JSON schema for analysis validation.") parser.add_argument("--output", required=True, help="Path to output JSON.") return parser.parse_args() def ensure_http_url(value: str) -> str: parsed = urlparse(value) if parsed.scheme not in {"http", "https"} or not parsed.netloc: raise ValueError("target_url must be an absolute http or https URL") return value def normalize_parameters(value: object) -> list[str]: if isinstance(value, str): items = [part.strip() for part in value.replace("/", ",").split(",")] elif isinstance(value, list): items = [str(part).strip() for part in value] else: raise ValueError("parameters must be a string or a list of strings") normalized: list[str] = [] seen: set[str] = set() for item in items: if not item: continue if item not in seen: seen.add(item) normalized.append(item) if not normalized: raise ValueError("parameters must not be empty") return normalized def validate_phase0(raw: dict) -> dict: """Validate and normalize Phase 0 input.""" missing = [field for field in ("target_url", "parameters") if not raw.get(field)] if missing: raise ValueError(f"missing required fields: {', '.join(missing)}") return { "target_url": ensure_http_url(str(raw["target_url"]).strip()), "parameters": normalize_parameters(raw["parameters"]), "fetch_example": str(raw.get("fetch_example", "")).strip(), "notes": raw.get("notes", []), } def validate_analysis_schema(analysis: dict, schema_path: str) -> list[str]: """Validate analysis_result.json against JSON schema if jsonschema is available.""" if not schema_path: return [] try: from jsonschema import Draft202012Validator schema = load_json(schema_path) errors = [e.message for e in Draft202012Validator(schema).iter_errors(analysis)] return errors except ImportError: return ["jsonschema not installed; skipping schema validation"] except FileNotFoundError: return [f"schema file not found: {schema_path}"] def main() -> int: args = parse_args() output_path = Path(args.output) # Phase 0 mode: validate raw input if args.input: raw = load_json(Path(args.input)) normalized = validate_phase0(raw) dump_json(output_path, normalized) print(json.dumps({"status": "ok", "output": str(output_path)}, ensure_ascii=False)) return 0 # Schema validation mode: validate analysis_result.json if args.analysis: analysis = load_json(Path(args.analysis)) # Check authorization.confirmed if present auth = analysis.get("authorization", {}) if auth and not auth.get("confirmed"): print("[ERROR] authorization.confirmed must be true") return 2 schema_path = args.schema or str(Path(__file__).parents[1] / "schemas" / "analysis_result.schema.json") errors = validate_analysis_schema(analysis, schema_path) if errors: for error in errors: print(f"[ERROR] {error}") return 2 dump_json(output_path, {"status": "ok", "analysis": analysis}) print(f"[OK] input validated: {output_path}") return 0 print("[ERROR] either --input or --analysis must be specified") return 1 if __name__ == "__main__": raise SystemExit(main())