|
29 | 29 | from scanpipe import pipes |
30 | 30 | from scanpipe.models import CodebaseRelation |
31 | 31 | from scanpipe.models import CodebaseResource |
| 32 | +from scanpipe.models import DiscoveredPackage |
32 | 33 | from scanpipe.pipes import flag |
33 | 34 | from scanpipe.pipes import get_resource_diff_ratio |
34 | 35 | from scanpipe.pipes import js |
@@ -840,3 +841,100 @@ def _map_javascript_path_resource( |
840 | 841 | extra_data=extra_data, |
841 | 842 | ) |
842 | 843 | return len(transpiled) |
| 844 | + |
| 845 | + |
| 846 | +def map_javascript_npm_lookup(project, logger=None): |
| 847 | + """Map unmatched ``node_modules`` files.""" |
| 848 | + project_directories = project.codebaseresources.directories().only("path") |
| 849 | + project_files = project.codebaseresources.files().only("path") |
| 850 | + |
| 851 | + to_directories_key = ( |
| 852 | + project_directories.to_codebase() |
| 853 | + .no_status() |
| 854 | + .filter(path__regex=r"^.*\/node_modules\/(?!.*\/)") |
| 855 | + .distinct() |
| 856 | + ) |
| 857 | + |
| 858 | + to_resources = ( |
| 859 | + project_files.to_codebase() |
| 860 | + .no_status() |
| 861 | + .filter(path__regex=r"^.*\/node_modules\/.*$") |
| 862 | + ) |
| 863 | + |
| 864 | + if not to_directories_key: |
| 865 | + logger("No unmatched ``node_modules`` file is available. Skipping.") |
| 866 | + return |
| 867 | + |
| 868 | + resource_count = to_resources.count() |
| 869 | + |
| 870 | + if logger: |
| 871 | + logger( |
| 872 | + f"Mapping {resource_count:,d} to/ resources using javascript map " |
| 873 | + f"against from/ codebase" |
| 874 | + ) |
| 875 | + |
| 876 | + to_resources_index = pathmap.build_index( |
| 877 | + to_resources.values_list("id", "path"), with_subpaths=True |
| 878 | + ) |
| 879 | + |
| 880 | + resource_iterator = to_directories_key.iterator(chunk_size=2000) |
| 881 | + last_percent = 0 |
| 882 | + map_count = 0 |
| 883 | + start_time = timer() |
| 884 | + |
| 885 | + for resource_index, to_directory in enumerate(resource_iterator): |
| 886 | + last_percent = pipes.log_progress( |
| 887 | + logger, |
| 888 | + resource_index, |
| 889 | + resource_count, |
| 890 | + last_percent, |
| 891 | + increment_percent=10, |
| 892 | + start_time=start_time, |
| 893 | + ) |
| 894 | + map_count += _map_javascript_npm_lookup_resource( |
| 895 | + to_directory, |
| 896 | + to_resources, |
| 897 | + to_resources_index, |
| 898 | + project, |
| 899 | + ) |
| 900 | + |
| 901 | + logger(f"{map_count:,d} resource(s) mapped") |
| 902 | + |
| 903 | + |
| 904 | +def _map_javascript_npm_lookup_resource( |
| 905 | + to_directory, |
| 906 | + to_resources, |
| 907 | + to_resources_index, |
| 908 | + project, |
| 909 | +): |
| 910 | + """Map unmatched ``node_modules`` files.""" |
| 911 | + purl = js.get_purl_from_node_module(to_directory) |
| 912 | + matched = to_resources.filter(path__startswith=to_directory.path) |
| 913 | + if not matched: |
| 914 | + return 0 |
| 915 | + |
| 916 | + try: |
| 917 | + package = project.discoveredpackages.get( |
| 918 | + type=purl.type, |
| 919 | + namespace="" if not purl.namespace else purl.namespace, |
| 920 | + name=purl.name, |
| 921 | + version=purl.version, |
| 922 | + ) |
| 923 | + package.add_resources(matched) |
| 924 | + |
| 925 | + except DiscoveredPackage.DoesNotExist: |
| 926 | + if results := purldb.fetch_package(purl=str(purl)): |
| 927 | + package_data = results[0] |
| 928 | + package_data.pop("uuid", None) |
| 929 | + package_data.pop("dependencies", None) |
| 930 | + |
| 931 | + package = pipes.update_or_create_package( |
| 932 | + project=project, |
| 933 | + package_data=package_data, |
| 934 | + codebase_resources=matched, |
| 935 | + ) |
| 936 | + else: |
| 937 | + return 0 |
| 938 | + |
| 939 | + matched.update(status=flag.NPM_LOOKUP) |
| 940 | + return len(matched) |
0 commit comments