Source code for bsmart.scans

import pkgutil
import ast
import json
import importlib.util
import sys
from pathlib import Path
import os 
def _extract_metadata(source):
    """
    Parse the source code and return the Python value assigned to __meta__,
    using ast to avoid executing code. Returns None if not found.
    """
    try:
        # Parse module into an AST
        module = ast.parse(source)
    except SyntaxError as e:  
        return {"Syntax error" : str(e)}, None

    documentation =  ast.get_docstring(module)  # returns None if there is none

    # Look for top-level assignments to __meta__
    for node in module.body:
        # We only consider simple Assign nodes (not AugAssign, Not annotated assigns in 3.5)
        if isinstance(node, ast.Assign):
            # node.targets can be a list; check each
            for target in node.targets:
                if isinstance(target, ast.Name) and target.id == "__meta__":
                    # node.value is an AST expression for the RHS
                    try:
                        # Safely evaluate literal structures (dicts, lists, strings, numbers, booleans, None)
                        value = ast.literal_eval(node.value)
                        return value, documentation
                    except Exception:
                        # If literal_eval fails (e.g. non-literal code), return error marker
                        return {"error": "Non-literal __meta__ value; cannot safely evaluate."}, None
    return {}, documentation

[docs] def get_available_scans(search_target=None, with_meta:bool = False) -> dict: """ Return a list of available scan modules and their metadata. search_target can be: - None: searches the current package (bsmart.scans) - A module object (e.g. bsmart.tools): searches that package - A dictionary path (str): searches that directory (e.g. ./Tools) """ paths = None prefix = "" base_import = None # 1. Determine paths and prefix based on input type if search_target is None: # Default: default to this package (bsmart.scans) # We avoid using the global __path__ directly if possible, or use it if we are sure. # Ideally, we import ourselves to be sure? # But __path__ is available in scope. paths = __path__ prefix = __name__ + "." base_import = None elif hasattr(search_target, '__path__'): # It's a package module (e.g. bsmart.tools) paths = search_target.__path__ prefix = search_target.__name__ + "." base_import = search_target.__name__ # or None? elif isinstance(search_target, str): # Assume it's a directory path (legacy/local tools) if os.path.isdir(search_target): paths = [search_target] prefix = "" base_import = search_target else: # Maybe it's a package name string? Not handling for now unless requested. return {} else: # Unknown input type return {} scans = {} # 2. Walk packages for module_finder, name, ispkg in pkgutil.walk_packages(paths, prefix=prefix): if ispkg: continue # 3. Determine scan_id # internal scans (bsmart.scans.X) -> id = X # tools (bsmart.tools.Y) -> id = Y # local files (X) -> id = X if prefix and name.startswith(prefix): scan_id = name[len(prefix):] else: scan_id = name scan_entry = {"module": name, "import_path": base_import} # 4. Try to find path for display try: if hasattr(module_finder, 'path'): # Construct valid path if possible, mostly for user info scan_entry['path'] = str(Path(module_finder.path) / (name.split('.')[-1] + ".py")) else: scan_entry['path'] = "Zip/Unknown" except: scan_entry['path'] = "Unknown" # 5. Extract metadata if requested if with_meta: source = None try: # Try to get source using the finder/loader if hasattr(module_finder, 'find_spec'): spec = module_finder.find_spec(name) if spec and spec.loader: source = spec.loader.get_source(name) elif hasattr(module_finder, 'find_module'): loader = module_finder.find_module(name) if loader: source = loader.get_source(name) except Exception: pass if source: meta, docs = _extract_metadata(source) scan_entry['meta'] = meta if docs is not None: scan_entry['documentation'] = docs scans[scan_id] = scan_entry return scans