|
| 1 | +# -*- coding: utf_8 -*- |
| 2 | +"""Extract packages from APK.""" |
| 3 | +import logging |
| 4 | + |
| 5 | +from mobsf.MobSF.utils import ( |
| 6 | + PKG_REGEX, |
| 7 | +) |
| 8 | + |
| 9 | + |
| 10 | +logger = logging.getLogger(__name__) |
| 11 | + |
| 12 | + |
| 13 | +def merge_common_packages(items): |
| 14 | + """Merge common packages.""" |
| 15 | + items = list(items) |
| 16 | + items.sort() # Sort items lexicographically |
| 17 | + merged = [] |
| 18 | + for item in items: |
| 19 | + if not merged or not item.startswith(merged[-1] + '.'): |
| 20 | + merged.append(item) |
| 21 | + return merged |
| 22 | + |
| 23 | + |
| 24 | +def extract_packages(file_data): |
| 25 | + """Extract package names from file data.""" |
| 26 | + packages = set() |
| 27 | + try: |
| 28 | + for item in file_data: |
| 29 | + # tuple has file path and file content |
| 30 | + # we are interested in file content's first line |
| 31 | + pkg = item[1].split('\n')[0] |
| 32 | + match = PKG_REGEX.search(pkg) |
| 33 | + if match and match.group(1) != '_COROUTINE': |
| 34 | + packages.add(match.group(1)) |
| 35 | + packages = merge_common_packages(packages) |
| 36 | + except Exception: |
| 37 | + logger.exception('Extracting packages from file data') |
| 38 | + return sorted(packages) |
| 39 | + |
| 40 | + |
| 41 | +def get_group_name(file_name, group): |
| 42 | + """Get group and name from file name.""" |
| 43 | + parts = file_name.split('_') |
| 44 | + |
| 45 | + if parts and len(parts) == 2: |
| 46 | + group, name = parts[0], parts[1] |
| 47 | + else: |
| 48 | + name = file_name.replace('_', '-') |
| 49 | + if name.startswith('kotlinx-'): |
| 50 | + group = 'org.jetbrains.kotlinx' |
| 51 | + |
| 52 | + return group, name |
| 53 | + |
| 54 | + |
| 55 | +def android_sbom(app_dir): |
| 56 | + """Extract SBOM from files.""" |
| 57 | + sbom = set() |
| 58 | + for vfile in app_dir.rglob('*.version'): |
| 59 | + try: |
| 60 | + dependency = vfile.stem |
| 61 | + group, name = '', '' |
| 62 | + version = vfile.read_text().strip() or '' |
| 63 | + version = 'dynamic' if version.startswith('task') else version |
| 64 | + |
| 65 | + if '_' in dependency: |
| 66 | + group, name = get_group_name(dependency, group) |
| 67 | + sbom.add(f'{group}:{name}@{version}') |
| 68 | + except Exception: |
| 69 | + pass |
| 70 | + return sorted(sbom) |
| 71 | + |
| 72 | + |
| 73 | +def sbom(app_dir, file_data): |
| 74 | + """Extract SBOM from version files and decompiled source code.""" |
| 75 | + return { |
| 76 | + 'sbom_versioned': android_sbom(app_dir), |
| 77 | + 'sbom_packages': extract_packages(file_data), |
| 78 | + } |
0 commit comments