Skip to content

Commit

Permalink
add javascript_alias_mapping_strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
ipeleg committed Sep 13, 2023
1 parent 05cf950 commit a63afa5
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 0 deletions.
Empty file.
8 changes: 8 additions & 0 deletions checkov/common/sca/reachability/alias_mapping_strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from abc import ABC, abstractmethod
from typing import List, Dict


class AliasMappingStrategy(ABC):
@abstractmethod
def create_alias_mapping(self, root_dir: str, relevant_packages: List[str]) -> Dict[str, List[str]]:
pass
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from __future__ import annotations

import os.path
from abc import ABC
from typing import List, Dict, Set, Any
import re
import json
import os

MODULE_EXPORTS_PATTERN = r'module\.exports\s*=\s*({.*?});'
EXPORT_DEFAULT_PATTERN = r'export\s*default\s*({.*?});'


class JavascriptAliasMappingStrategy(ABC):

def _parse_export(self, file_content: str, pattern: str) -> Dict[str, Any] | None:
module_export_match = re.search(pattern, file_content, re.DOTALL)

if module_export_match:
module_exports_str = module_export_match.group(1)
module_exports_str = re.sub(r'([{\s,])(\w+):', r'\1"\2":', module_exports_str).replace("'", "\"")
print(module_exports_str)
module_exports: Dict[str, Any]= json.loads(module_exports_str)
return module_exports
return None

def parse_webpack_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
module_exports_json = self._parse_export(file_content, MODULE_EXPORTS_PATTERN)

if module_exports_json:
aliases = module_exports_json.get("resolve", {}).get("alias", {})
for imported_name in aliases:
package_name = aliases[imported_name]
if package_name in relevant_packages:
alias_mapping.setdefault(package_name, []).append(imported_name)

def parse_tsconfig_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
tsconfig_json = json.loads(file_content)
paths = tsconfig_json.get("compilerOptions", {}).get("paths", {})
for imported_name in paths:
for package_relative_path in paths[imported_name]:
package_name = os.path.basename(package_relative_path)
if package_name in relevant_packages:
alias_mapping.setdefault(package_name, []).append(imported_name)


def parse_babel_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
babelrc_json = json.loads(file_content)
plugins = babelrc_json.get("plugins", {})
for plugin in plugins:
if len(plugin) > 1:
plugin_object = plugin[1]
aliases = plugin_object.get("alias", {})
for imported_name in aliases:
package_name = aliases[imported_name]
if package_name in relevant_packages:
alias_mapping.setdefault(package_name, []).append(imported_name)

def parse_rollup_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
export_default_match = re.search(EXPORT_DEFAULT_PATTERN, file_content, re.DOTALL)

if export_default_match:
export_default_str = export_default_match.group(1)
export_default_str = re.sub(r'([{\s,])(\w+):', r'\1"\2":', export_default_str).replace("'", "\"")
export_default_str = re.sub(r'\s+', '', export_default_str)

# Define a regular expression pattern to match the elements within the "plugins" list
pattern = r'alias\(\{[^)]*\}\)'

# Use the findall() function to find all matches of the pattern in the input string
matches = re.findall(pattern, export_default_str)
for alias_object_str in matches:
alias_object = json.loads(alias_object_str[6:-1]) # removing 'alias(' and ')'
print(alias_object)
for entry in alias_object.get("entries", []):
alias_mapping.setdefault(entry["replacement"], []).append(entry["find"])


def parse_package_json_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
package_json = json.loads(file_content)
aliases: Dict[str, str] = dict()
if "alias" in package_json:
aliases.update(package_json["alias"])
if package_json.get("aliasify", {}).get("aliases"):
aliases.update(package_json["aliasify"]["aliases"])
for imported_name in aliases:
alias_mapping.setdefault(aliases[imported_name], []).append(imported_name)

def parse_snowpack_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
module_exports_json = self._parse_export(file_content, MODULE_EXPORTS_PATTERN)

if module_exports_json:
aliases = module_exports_json.get("alias", {})
for imported_name in aliases:
package_name = aliases[imported_name]
if package_name in relevant_packages:
alias_mapping.setdefault(package_name, []).append(imported_name)

def parse_vite_file(self, alias_mapping: Dict[str, List[str]], file_content: str, relevant_packages: Set[str])\
-> None:
export_default_match = self._parse_export(file_content, EXPORT_DEFAULT_PATTERN)

if export_default_match:
aliases = export_default_match.get("resolve", {}).get("alias", {})
for imported_name in aliases:
package_name = aliases[imported_name]
if package_name in relevant_packages:
alias_mapping.setdefault(package_name, []).append(imported_name)

def create_alias_mapping(self, root_dir: str, relevant_packages: List[str]) -> Dict[str, List[str]]:
return dict()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from checkov.common.sca.reachability.javascript.javascript_alias_mapping_strategy import JavascriptAliasMappingStrategy


def test_parse_webpack_file():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
"module.exports = {" \
" resolve: {" \
" alias: {" \
" ax: 'axios'" \
" }" \
" }" \
"};"
strategy_object.parse_webpack_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}


def test_parse_babel_file():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
'{' \
' "plugins": [' \
' ["module-resolver", {' \
' "alias": {' \
' "ax": "axios"' \
' }' \
' }]' \
' ]' \
'}'
strategy_object.parse_babel_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}


def test_parse_rollup_file():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
"import alias from '@rollup/plugin-alias';" \
"export default {" \
" plugins: [" \
" alias({" \
" entries: [" \
" { find: 'ax', replacement: 'axios' }" \
" ]" \
" })" \
" ]" \
"};"
strategy_object.parse_rollup_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}


def test_parse_package_json_alias():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
'{' \
' "alias": {' \
' "ax": "axios"' \
' }' \
'}'
strategy_object.parse_package_json_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}


def test_parse_package_json_aliasify():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
'{' \
' "aliasify": {' \
' "aliases": {' \
' "ax": "axios"' \
' }' \
' }' \
'}'
strategy_object.parse_package_json_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}


def test_parse_snowpack():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
'module.exports = {' \
' alias: {' \
' "ax": "axios"' \
' }' \
'};'
strategy_object.parse_snowpack_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}


def test_parse_vite():
strategy_object = JavascriptAliasMappingStrategy()
alias_mapping: dict[str, list[str]] = dict()
file_content = \
'export default {' \
' resolve: {' \
' alias: {' \
' "ax": "axios"' \
' }' \
' }' \
'};'
strategy_object.parse_vite_file(alias_mapping, file_content, {'axios'})
assert alias_mapping == {"axios": ["ax"]}

0 comments on commit a63afa5

Please sign in to comment.