#!/usr/bin/env python
"""
Script to extract the list of javascript packages from a javascript map file.

This script generate a Node "package.json" file content in order to facilitate 
the identification of dependency confusion attack vectors.

Sources:
    https://developer.chrome.com/blog/sourcemaps/

Dependency: pip install termcolor
"""
import argparse
import json
from termcolor import colored

PACKAGE_JSON_TPL = """{
  "name": "sample",
  "version": "1.0.0",
  "description": "Sample",
  "main": "index.js",
  "repository": "https://github.com/test",
  "author": "righettod",
  "license": "MIT",
  "dependencies": { %s }
}"""


def extract_packages_list(map_file_json_content):
    packages = []
    marker = "node_modules/"
    marker_len = len(marker)
    if "sources" in map_file_json_content:
        for line in map_file_json_content["sources"]:
            work = line
            if marker in work:
                pos = work.find(marker) + marker_len
                work = work[pos:]
                parts = work.split("/")
                if parts[0].startswith("@"):
                    package = "/".join(parts[:2])
                else:
                    package = parts[0]
                if package not in packages:
                    packages.append(package)
    return packages


def generate_dependencies_definition(packages_list):
    defs = []
    added = []
    for package in packages_list:
        if package not in added:
            defs.append(f'"{package}": "*"')
            added.append(package)
    defs.sort()
    return defs


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Script to extract the list of javascript packages from a javascript map file.")
    required_params = parser.add_argument_group("required named arguments")
    required_params.add_argument("-f", nargs="+", action="append", dest="map_file", help="Path to the map file. Specify it multiple time to process several map files.", required=True)
    args = parser.parse_args()
    map_files = [item for sublist in args.map_file for item in sublist]
    all_packages = []
    for map_file in map_files:
        with open(map_file, mode="r", encoding="utf-8") as f:
            map_file_json_content = json.load(f)
        all_packages.extend(extract_packages_list(map_file_json_content))
    dependencies_defs = generate_dependencies_definition(all_packages)
    pkg_json_str = PACKAGE_JSON_TPL % ",".join(dependencies_defs)
    print(colored("[i] Use the command 'npm install --ignore-scripts --force' against the 'package.json' file content generated.", "cyan"))
    print(colored("[+] Package.json file content:", "yellow"))
    print(json.dumps(json.loads(pkg_json_str), indent=2))
