Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ It can:
- `misp-modules` running and reachable (default: `http://127.0.0.1:6666`)
- Python dependency:
- `requests`
- Network access to `raw.githubusercontent.com` to fetch MISP's `describeTypes.json`
(`--describe-types-url`), used to validate/guess attribute types and for
`--list-supported-types`/`--list-active-modules`/`--verbose-types`. This fetch is
cached locally (see `--cache-file`/`--cache-ttl-seconds`) and is skipped entirely
when you pass an explicit `--type`, so fully-specified queries work without it
(e.g. in air-gapped environments).

Install dependencies:

Expand Down
44 changes: 38 additions & 6 deletions bin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,27 @@ def purge_cache(cache_path: str) -> int:
return 0


def get_describe_types(describe_types_url: str, cache_path: str, cache_ttl_seconds: int) -> Dict[str, Any]:
"""Fetch MISP's describeTypes.json, reusing a cached copy within the TTL to avoid
hitting raw.githubusercontent.com on every invocation."""
cache_key = f"describe_types:{describe_types_url}"
now = int(time.time())
try:
cache = load_cache(cache_path)
except Exception:
cache = {"entries": {}}
cached = get_cached_response(cache, cache_key, now, cache_ttl_seconds)
if cached is not None:
return cached["response"]
describe_types = fetch_describe_types(describe_types_url)
cache.setdefault("entries", {})[cache_key] = {"cached_at": now, "response": describe_types}
try:
save_cache(cache_path, cache)
except Exception:
pass
return describe_types


def make_cache_key(base_url: str, module_name: str, attr_type: str, value: str, module_config: Dict[str, Any]) -> str:
key_payload = {
"base_url": base_url.rstrip("/"),
Expand Down Expand Up @@ -848,9 +869,19 @@ def main() -> int:
return 1

valid_types = set()
if args.list_supported_types or args.verbose_types or not args.list_active_modules:
need_describe_types = args.list_supported_types or args.verbose_types or not args.list_active_modules
# A fully explicit --type query doesn't need MISP's describeTypes.json (fetched from
# raw.githubusercontent.com) to know what to do, so skip that network dependency and
# trust the user-provided type in that case.
skip_for_explicit_type = (
need_describe_types
and args.attr_type
and not args.list_supported_types
and not args.list_active_modules
)
if need_describe_types and not skip_for_explicit_type:
try:
describe_types = fetch_describe_types(args.describe_types_url)
describe_types = get_describe_types(args.describe_types_url, args.cache_file, args.cache_ttl_seconds)
valid_types = get_valid_types(describe_types)
except Exception as e:
print(f"[!] Unable to fetch describeTypes.json: {e}", file=sys.stderr)
Expand Down Expand Up @@ -921,10 +952,11 @@ def main() -> int:
log("No likely MISP attribute type could be guessed from the input.")
return 1

candidate_types = [(t, r) for t, r in candidate_types if t in valid_types]
if not candidate_types:
log("No valid MISP attribute type found.")
return 1
if valid_types:
candidate_types = [(t, r) for t, r in candidate_types if t in valid_types]
if not candidate_types:
log("No valid MISP attribute type found.")
return 1

if not args.attr_type and not args.all_guesses:
candidate_types = candidate_types[:1]
Expand Down