fosslight-source 2.3.3__tar.gz → 2.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {fosslight_source-2.3.3/src/fosslight_source.egg-info → fosslight_source-2.3.4}/PKG-INFO +2 -1
  2. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/pyproject.toml +2 -1
  3. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/cli.py +5 -8
  4. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/run_scancode.py +191 -58
  5. {fosslight_source-2.3.3 → fosslight_source-2.3.4/src/fosslight_source.egg-info}/PKG-INFO +2 -1
  6. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source.egg-info/requires.txt +1 -0
  7. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/LICENSE +0 -0
  8. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/MANIFEST.in +0 -0
  9. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/README.md +0 -0
  10. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/setup.cfg +0 -0
  11. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/__init__.py +0 -0
  12. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_help.py +0 -0
  13. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_kb_client.py +0 -0
  14. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_license_matched.py +0 -0
  15. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_merge.py +0 -0
  16. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_parsing_scancode_file_item.py +0 -0
  17. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_parsing_scanoss_file.py +0 -0
  18. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/_scan_item.py +0 -0
  19. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/run_manifest_extractor.py +0 -0
  20. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/run_scanoss.py +0 -0
  21. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source/run_spdx_extractor.py +0 -0
  22. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source.egg-info/SOURCES.txt +0 -0
  23. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source.egg-info/dependency_links.txt +0 -0
  24. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source.egg-info/entry_points.txt +0 -0
  25. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/src/fosslight_source.egg-info/top_level.txt +0 -0
  26. {fosslight_source-2.3.3 → fosslight_source-2.3.4}/tests/test_tox.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_source
3
- Version: 2.3.3
3
+ Version: 2.3.4
4
4
  Summary: FOSSLight Source Scanner
5
5
  Author: LG Electronics
6
6
  License-Expression: Apache-2.0
@@ -26,6 +26,7 @@ Requires-Dist: wheel>=0.38.1
26
26
  Requires-Dist: intbitset
27
27
  Requires-Dist: fosslight_binary>=5.1.22
28
28
  Requires-Dist: scancode-toolkit>=32.0.2
29
+ Requires-Dist: lxml>=6.0.1
29
30
  Requires-Dist: cryptography<49; platform_system == "Darwin" and platform_machine == "x86_64"
30
31
  Requires-Dist: fingerprints==1.2.3
31
32
  Requires-Dist: normality==2.6.1
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "fosslight_source"
10
- version = "2.3.3"
10
+ version = "2.3.4"
11
11
  description = "FOSSLight Source Scanner"
12
12
  readme = "README.md"
13
13
  license = "Apache-2.0"
@@ -35,6 +35,7 @@ dependencies = [
35
35
  "intbitset",
36
36
  "fosslight_binary>=5.1.22",
37
37
  "scancode-toolkit>=32.0.2",
38
+ "lxml>=6.0.1",
38
39
  # cryptography 49.x does not provide macOS x86_64 wheels, causing source builds to require OpenSSL/pkg-config.
39
40
  "cryptography<49; platform_system == 'Darwin' and platform_machine == 'x86_64'",
40
41
  "fingerprints==1.2.3",
@@ -572,7 +572,7 @@ def run_scanners(
572
572
  excluded_path_without_dot,
573
573
  excluded_files,
574
574
  cnt_file_except_skipped) = get_excluded_paths(path_to_scan, path_to_exclude_with_filename)
575
- logger.debug(f"Skipped paths: {excluded_path_with_default_exclusion}")
575
+ logger.debug(f"Skipped paths count: {len(excluded_path_with_default_exclusion)}")
576
576
 
577
577
  if not selected_scanner:
578
578
  selected_scanner = ALL_MODE
@@ -580,8 +580,8 @@ def run_scanners(
580
580
  success, result_log[RESULT_KEY], scancode_result, license_list = run_scan(
581
581
  path_to_scan, output_file_name, write_json_file, num_cores, True,
582
582
  print_matched_text, formats, called_by_cli, time_out, correct_mode,
583
- correct_filepath, excluded_path_with_default_exclusion,
584
- excluded_files, hide_progress,
583
+ correct_filepath, path_to_exclude,
584
+ hide_progress=hide_progress,
585
585
  )
586
586
  excluded_files = set(excluded_files) if excluded_files else set()
587
587
  if selected_scanner in ['scanoss', ALL_MODE]:
@@ -598,7 +598,7 @@ def run_scanners(
598
598
  run_kb_msg = f"KB({kb_url}) Unreachable"
599
599
 
600
600
  spdx_downloads, manifest_licenses = metadata_collector(path_to_scan, excluded_files)
601
- merged_result, kb_status_message, kb_requested_count, kb_returned_count = merge_results(
601
+ merged_result, kb_status_message, kb_requested_count, _ = merge_results(
602
602
  scancode_result, scanoss_result, spdx_downloads,
603
603
  path_to_scan, run_kb, manifest_licenses, excluded_files,
604
604
  hide_progress, kb_url, kb_token,
@@ -606,10 +606,7 @@ def run_scanners(
606
606
  if kb_status_message:
607
607
  run_kb_msg = f"KB({kb_url}) {kb_status_message}"
608
608
  elif run_kb and kb_requested_count > 0:
609
- run_kb_msg = (
610
- f"KB({kb_url}) response : {kb_returned_count}/"
611
- f" requested: {kb_requested_count}"
612
- )
609
+ run_kb_msg = f"KB({kb_url}) : Completed"
613
610
  mark_oss_info_correction_files_as_excluded(merged_result)
614
611
  scan_item = create_report_file(start_time, merged_result, license_list, scanoss_result, selected_scanner,
615
612
  print_matched_text, output_path, output_files, output_extensions, correct_mode,
@@ -22,7 +22,7 @@ from fosslight_util.exclude import (
22
22
  PACKAGE_DIRECTORY,
23
23
  )
24
24
  from commoncode.fileset import is_included
25
- from typing import Tuple, Iterable
25
+ from typing import Callable, Tuple
26
26
 
27
27
  logger = logging.getLogger(constant.LOGGER_NAME)
28
28
  warnings.filterwarnings("ignore", category=FutureWarning)
@@ -62,6 +62,96 @@ def _apply_scancode_unset_workaround(kwargs: dict) -> None:
62
62
  logger.debug("scancode UNSET workaround skipped: %s", ex)
63
63
 
64
64
 
65
+ _WILDCARD_EXTENSIONS = {
66
+ "png", "mp3", "wav", "comp", "bin", "o", "db", "tflite",
67
+ "ttf", "exe", "dll", "jpg", "jpeg", "gif",
68
+ "zip", "tar", "tgz", "gz",
69
+ "bmp", "webp", "ico",
70
+ } | {ext.lower() for ext in EXCLUDE_FILE_EXTENSION}
71
+ _SKIP_DIR_NAMES = frozenset(name.lower() for name in PACKAGE_DIRECTORY + EXCLUDE_DIRECTORY)
72
+ _SKIP_EXTS = frozenset(ext.lower() for ext in EXCLUDE_FILE_EXTENSION)
73
+
74
+
75
+ def _normalize_custom_pattern(pattern: str, abs_path_to_scan: str) -> set:
76
+ pat = pattern.replace('\\', '/').strip()
77
+ if not pat:
78
+ return set()
79
+
80
+ patterns_to_add = {pat}
81
+
82
+ if pat.endswith("/**"):
83
+ base = pat[:-3].rstrip("/")
84
+ if base:
85
+ patterns_to_add.add(base)
86
+ elif pat.endswith("/*"):
87
+ base = pat[:-2].rstrip("/")
88
+ if base:
89
+ patterns_to_add.add(base)
90
+ patterns_to_add.add(f"{base}/**")
91
+ elif pat.endswith("/"):
92
+ base = pat.rstrip("/")
93
+ if base:
94
+ patterns_to_add.add(base)
95
+ patterns_to_add.add(f"{base}/**")
96
+ patterns_to_add.add(f"{base}/*")
97
+ else:
98
+ full_path = os.path.join(abs_path_to_scan, pat)
99
+ if os.path.isdir(full_path):
100
+ patterns_to_add.add(f"{pat}/**")
101
+ patterns_to_add.add(f"{pat}/*")
102
+
103
+ return patterns_to_add
104
+
105
+
106
+ def _expand_custom_exclude_pattern(pattern: str, abs_path_to_scan: str) -> set:
107
+ exclude_path_normalized = os.path.normpath(
108
+ pattern.replace('\\', '/').strip()
109
+ ).replace("\\", "/")
110
+ if not exclude_path_normalized:
111
+ return set()
112
+
113
+ patterns = set(_normalize_custom_pattern(exclude_path_normalized, abs_path_to_scan))
114
+
115
+ if exclude_path_normalized.endswith("/**"):
116
+ base_dir = exclude_path_normalized[:-3].rstrip("/")
117
+ if base_dir:
118
+ full_exclude_path = os.path.join(abs_path_to_scan, base_dir)
119
+ if os.path.isdir(full_exclude_path):
120
+ patterns.add(base_dir)
121
+ patterns.add(exclude_path_normalized)
122
+ else:
123
+ patterns.add(exclude_path_normalized)
124
+ return patterns
125
+
126
+ has_glob_chars = any(char in exclude_path_normalized for char in ['*', '?', '['])
127
+ if has_glob_chars:
128
+ patterns.add(exclude_path_normalized)
129
+ return patterns
130
+
131
+ full_exclude_path = os.path.join(abs_path_to_scan, exclude_path_normalized)
132
+ if os.path.isdir(full_exclude_path):
133
+ base_path = exclude_path_normalized.rstrip("/")
134
+ if base_path:
135
+ patterns.add(base_path)
136
+ patterns.add(f"{base_path}/**")
137
+ else:
138
+ patterns.add(exclude_path_normalized)
139
+ elif os.path.isfile(full_exclude_path):
140
+ ext = os.path.splitext(exclude_path_normalized)[1].lstrip('.').lower()
141
+ if ext in _WILDCARD_EXTENSIONS:
142
+ patterns.add(f"*.{ext}")
143
+ else:
144
+ patterns.add(f"**/{exclude_path_normalized}")
145
+ else:
146
+ ext = os.path.splitext(exclude_path_normalized)[1].lstrip('.').lower()
147
+ if ext in _WILDCARD_EXTENSIONS:
148
+ patterns.add(f"*.{ext}")
149
+ else:
150
+ patterns.add(exclude_path_normalized)
151
+
152
+ return patterns
153
+
154
+
65
155
  def _directory_ignore_pattern(dir_name: str) -> str:
66
156
  """Path-based glob for a directory name (avoids matching the scan root itself)."""
67
157
  normalized = dir_name.strip().strip("/").replace("\\", "/")
@@ -70,7 +160,10 @@ def _directory_ignore_pattern(dir_name: str) -> str:
70
160
  return f"**/{normalized}/**"
71
161
 
72
162
 
73
- def _default_scancode_coarse_ignore_patterns() -> frozenset:
163
+ def _default_scancode_coarse_ignore_patterns(
164
+ path_to_exclude: list = None,
165
+ abs_path_to_scan: str = ""
166
+ ) -> frozenset:
74
167
  """
75
168
  Coarse ignore patterns aligned with fosslight_util.get_excluded_paths() rules.
76
169
  Directory names use path-based globs (e.g. **/tests/**) so they do not match
@@ -83,73 +176,93 @@ def _default_scancode_coarse_ignore_patterns() -> frozenset:
83
176
  patterns.add(f"*.{ext}")
84
177
  for name in EXCLUDE_FILENAME:
85
178
  patterns.add(name)
179
+
180
+ for pattern in path_to_exclude or []:
181
+ if os.path.isabs(pattern):
182
+ pattern = os.path.relpath(pattern, abs_path_to_scan)
183
+ patterns.update(_expand_custom_exclude_pattern(pattern, abs_path_to_scan))
184
+
86
185
  return frozenset(patterns)
87
186
 
88
187
 
89
- def _is_covered_by_coarse_ignore(rel_path: str, coarse_patterns: Iterable[str]) -> bool:
90
- excludes = {pattern: "" for pattern in coarse_patterns}
188
+ def _to_excludes_dict(patterns) -> dict:
189
+ return {pattern: "exclude" for pattern in patterns}
190
+
191
+
192
+ def _is_path_covered(rel_path: str, excludes: dict) -> bool:
91
193
  return not is_included(rel_path, includes={}, excludes=excludes)
92
194
 
93
195
 
94
- def _add_path_to_exclude_pattern(
196
+ def _add_ignore_pattern(
95
197
  patterns: set,
96
- exclude_path: str,
97
- abs_path_to_scan: str,
198
+ excludes: dict,
199
+ pattern: str,
200
+ *,
201
+ sample_path: str = None,
202
+ ) -> bool:
203
+ if pattern in patterns:
204
+ return False
205
+ if sample_path and _is_path_covered(sample_path, excludes):
206
+ return False
207
+ patterns.add(pattern)
208
+ excludes[pattern] = "exclude"
209
+ return True
210
+
211
+
212
+ def _make_pre_scan_skip_filter(
98
213
  coarse_patterns: frozenset,
214
+ ) -> Tuple[dict, Callable[[str, str], bool], Callable[[str, str], bool]]:
215
+ excludes = _to_excludes_dict(coarse_patterns)
216
+
217
+ def should_skip_dir(dir_name: str, rel_dir: str) -> bool:
218
+ if dir_name.startswith('.'):
219
+ return True
220
+ if dir_name.lower() in _SKIP_DIR_NAMES:
221
+ return True
222
+ return _is_path_covered(f"{rel_dir}/_", excludes)
223
+
224
+ def should_skip_file(file_name: str, rel_path: str) -> bool:
225
+ if file_name.startswith('.'):
226
+ return True
227
+ ext = os.path.splitext(file_name)[1].lstrip('.').lower()
228
+ if ext in _SKIP_EXTS:
229
+ return True
230
+ return _is_path_covered(rel_path, excludes)
231
+
232
+ return excludes, should_skip_dir, should_skip_file
233
+
234
+
235
+ def _add_binary_ignore_patterns(
236
+ patterns: set,
237
+ excludes: dict,
238
+ binary_paths: list,
99
239
  ) -> None:
100
- exclude_path_normalized = os.path.normpath(exclude_path).replace("\\", "/")
240
+ extensions = set()
241
+ no_ext_paths = []
101
242
 
102
- if exclude_path_normalized.endswith("/**"):
103
- base_dir = exclude_path_normalized[:-3].rstrip("/")
104
- if base_dir:
105
- full_exclude_path = os.path.join(abs_path_to_scan, base_dir)
106
- if os.path.isdir(full_exclude_path):
107
- patterns.add(base_dir)
108
- patterns.add(exclude_path_normalized)
109
- else:
110
- patterns.add(exclude_path_normalized)
243
+ for rel_path in binary_paths:
244
+ ext = os.path.splitext(rel_path)[1].lstrip('.').lower()
245
+ if ext:
246
+ extensions.add(ext)
111
247
  else:
112
- patterns.add(exclude_path_normalized)
113
- return
248
+ no_ext_paths.append(rel_path)
114
249
 
115
- has_glob_chars = any(char in exclude_path_normalized for char in ['*', '?', '['])
116
- if has_glob_chars:
117
- patterns.add(exclude_path_normalized)
118
- return
250
+ for ext in extensions:
251
+ _add_ignore_pattern(patterns, excludes, f"*.{ext}")
119
252
 
120
- full_exclude_path = os.path.join(abs_path_to_scan, exclude_path_normalized)
121
- if os.path.isdir(full_exclude_path):
122
- base_path = exclude_path_normalized.rstrip("/")
123
- if base_path:
124
- patterns.add(base_path)
125
- patterns.add(f"{base_path}/**")
126
- else:
127
- patterns.add(exclude_path_normalized)
128
- elif os.path.isfile(full_exclude_path):
129
- if not _is_covered_by_coarse_ignore(exclude_path_normalized, coarse_patterns):
130
- patterns.add(f"**/{exclude_path_normalized}")
131
- else:
132
- patterns.add(exclude_path_normalized)
253
+ for rel_path in no_ext_paths:
254
+ _add_ignore_pattern(
255
+ patterns, excludes, f"**/{rel_path}", sample_path=rel_path
256
+ )
133
257
 
134
258
 
135
259
  def _build_scancode_ignore_patterns(
136
- path_to_exclude: list,
137
- abs_path_to_scan: str,
260
+ coarse_patterns: frozenset,
138
261
  binary_paths: list,
139
262
  ) -> tuple:
140
- coarse_patterns = _default_scancode_coarse_ignore_patterns()
141
263
  patterns = set(coarse_patterns)
142
-
143
- for path in path_to_exclude or []:
144
- if os.path.isabs(path):
145
- exclude_path = os.path.relpath(path, abs_path_to_scan)
146
- else:
147
- exclude_path = path
148
- _add_path_to_exclude_pattern(patterns, exclude_path, abs_path_to_scan, coarse_patterns)
149
-
150
- for rel_path in binary_paths:
151
- patterns.add(f"**/{rel_path}")
152
-
264
+ excludes = _to_excludes_dict(coarse_patterns)
265
+ _add_binary_ignore_patterns(patterns, excludes, binary_paths)
153
266
  return tuple(sorted(patterns))
154
267
 
155
268
 
@@ -198,8 +311,13 @@ def run_scan(
198
311
  output_json_file = ""
199
312
 
200
313
  if not called_by_cli:
201
- logger, _result_log = init_log(os.path.join(output_path, f"fosslight_log_src_{_file_time}.txt"),
202
- True, logging.INFO, logging.DEBUG, _PKG_NAME, path_to_scan, path_to_exclude)
314
+ log_file_path = os.path.join(
315
+ output_path, f"fosslight_log_src_{_file_time}.txt"
316
+ )
317
+ logger, _result_log = init_log(
318
+ log_file_path, True, logging.INFO, logging.DEBUG,
319
+ _PKG_NAME, path_to_scan, path_to_exclude
320
+ )
203
321
 
204
322
  logger.info(f"Tool Info : {_result_log['Tool Info']}")
205
323
 
@@ -214,21 +332,36 @@ def run_scan(
214
332
  pretty_params["output_file"] = output_file_name
215
333
  abs_path_to_scan = os.path.abspath(path_to_scan)
216
334
  binary_paths = []
217
- for root, _, files in os.walk(path_to_scan):
335
+ coarse_patterns = _default_scancode_coarse_ignore_patterns(
336
+ path_to_exclude, abs_path_to_scan
337
+ )
338
+ _, should_skip_dir, should_skip_file = _make_pre_scan_skip_filter(
339
+ coarse_patterns
340
+ )
341
+
342
+ for root, dirs, files in os.walk(path_to_scan):
343
+ rel_root = os.path.relpath(root, abs_path_to_scan).replace("\\", "/")
344
+ dirs[:] = [
345
+ d for d in dirs
346
+ if not should_skip_dir(
347
+ d, d if rel_root == "." else f"{rel_root}/{d}"
348
+ )
349
+ ]
218
350
  for name in files:
351
+ rel_path = name if rel_root == "." else f"{rel_root}/{name}"
352
+ if should_skip_file(name, rel_path):
353
+ continue
219
354
  full_path = os.path.join(root, name)
220
355
  try:
221
356
  if not check_binary(full_path, True):
222
357
  continue
223
358
  except Exception:
224
359
  continue
225
- rel_path = os.path.relpath(full_path, abs_path_to_scan)
226
- rel_norm = os.path.normpath(rel_path).replace("\\", "/")
227
- binary_paths.append(rel_norm)
228
- logger.debug(f"Excluded binary from scancode: {rel_norm}")
360
+ binary_paths.append(rel_path)
361
+ logger.debug(f"Excluded binary from scancode: {rel_path}")
229
362
 
230
363
  ignore_tuple = _build_scancode_ignore_patterns(
231
- path_to_exclude, abs_path_to_scan, binary_paths
364
+ coarse_patterns, binary_paths
232
365
  )
233
366
  logger.debug(f"Scancode ignore patterns: {len(ignore_tuple)}")
234
367
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_source
3
- Version: 2.3.3
3
+ Version: 2.3.4
4
4
  Summary: FOSSLight Source Scanner
5
5
  Author: LG Electronics
6
6
  License-Expression: Apache-2.0
@@ -26,6 +26,7 @@ Requires-Dist: wheel>=0.38.1
26
26
  Requires-Dist: intbitset
27
27
  Requires-Dist: fosslight_binary>=5.1.22
28
28
  Requires-Dist: scancode-toolkit>=32.0.2
29
+ Requires-Dist: lxml>=6.0.1
29
30
  Requires-Dist: cryptography<49; platform_system == "Darwin" and platform_machine == "x86_64"
30
31
  Requires-Dist: fingerprints==1.2.3
31
32
  Requires-Dist: normality==2.6.1
@@ -8,6 +8,7 @@ wheel>=0.38.1
8
8
  intbitset
9
9
  fosslight_binary>=5.1.22
10
10
  scancode-toolkit>=32.0.2
11
+ lxml>=6.0.1
11
12
  fingerprints==1.2.3
12
13
  normality==2.6.1
13
14
  tqdm