skylos 1.0.10__py3-none-any.whl → 2.5.2__py3-none-any.whl

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 (53) hide show
  1. skylos/__init__.py +9 -3
  2. skylos/analyzer.py +674 -168
  3. skylos/cfg_visitor.py +60 -0
  4. skylos/cli.py +719 -235
  5. skylos/codemods.py +277 -0
  6. skylos/config.py +50 -0
  7. skylos/constants.py +78 -0
  8. skylos/gatekeeper.py +147 -0
  9. skylos/linter.py +18 -0
  10. skylos/rules/base.py +20 -0
  11. skylos/rules/danger/calls.py +119 -0
  12. skylos/rules/danger/danger.py +157 -0
  13. skylos/rules/danger/danger_cmd/cmd_flow.py +75 -0
  14. skylos/rules/danger/danger_fs/__init__.py +0 -0
  15. skylos/rules/danger/danger_fs/path_flow.py +79 -0
  16. skylos/rules/danger/danger_net/__init__.py +0 -0
  17. skylos/rules/danger/danger_net/ssrf_flow.py +80 -0
  18. skylos/rules/danger/danger_sql/__init__.py +0 -0
  19. skylos/rules/danger/danger_sql/sql_flow.py +245 -0
  20. skylos/rules/danger/danger_sql/sql_raw_flow.py +96 -0
  21. skylos/rules/danger/danger_web/__init__.py +0 -0
  22. skylos/rules/danger/danger_web/xss_flow.py +170 -0
  23. skylos/rules/danger/taint.py +110 -0
  24. skylos/rules/quality/__init__.py +0 -0
  25. skylos/rules/quality/complexity.py +95 -0
  26. skylos/rules/quality/logic.py +96 -0
  27. skylos/rules/quality/nesting.py +101 -0
  28. skylos/rules/quality/structure.py +99 -0
  29. skylos/rules/secrets.py +325 -0
  30. skylos/server.py +554 -0
  31. skylos/visitor.py +502 -90
  32. skylos/visitors/__init__.py +0 -0
  33. skylos/visitors/framework_aware.py +437 -0
  34. skylos/visitors/test_aware.py +74 -0
  35. skylos-2.5.2.dist-info/METADATA +21 -0
  36. skylos-2.5.2.dist-info/RECORD +42 -0
  37. {skylos-1.0.10.dist-info → skylos-2.5.2.dist-info}/WHEEL +1 -1
  38. {skylos-1.0.10.dist-info → skylos-2.5.2.dist-info}/top_level.txt +0 -1
  39. skylos-1.0.10.dist-info/METADATA +0 -8
  40. skylos-1.0.10.dist-info/RECORD +0 -21
  41. test/compare_tools.py +0 -604
  42. test/diagnostics.py +0 -364
  43. test/sample_repo/app.py +0 -13
  44. test/sample_repo/sample_repo/commands.py +0 -81
  45. test/sample_repo/sample_repo/models.py +0 -122
  46. test/sample_repo/sample_repo/routes.py +0 -89
  47. test/sample_repo/sample_repo/utils.py +0 -36
  48. test/test_skylos.py +0 -456
  49. test/test_visitor.py +0 -220
  50. {test → skylos/rules}/__init__.py +0 -0
  51. {test/sample_repo → skylos/rules/danger}/__init__.py +0 -0
  52. {test/sample_repo/sample_repo → skylos/rules/danger/danger_cmd}/__init__.py +0 -0
  53. {skylos-1.0.10.dist-info → skylos-2.5.2.dist-info}/entry_points.txt +0 -0
File without changes
@@ -0,0 +1,437 @@
1
+ import ast
2
+ import fnmatch
3
+ from pathlib import Path
4
+
5
+ FRAMEWORK_DECORATORS = [
6
+ "@*.route",
7
+ "@*.get",
8
+ "@*.post",
9
+ "@*.put",
10
+ "@*.delete",
11
+ "@*.patch",
12
+ "@*.before_request",
13
+ "@*.after_request",
14
+ "@*.errorhandler",
15
+ "@*.teardown_*",
16
+ "@*.head",
17
+ "@*.options",
18
+ "@*.trace",
19
+ "@*.websocket",
20
+ "@*.middleware",
21
+ "@*.on_event",
22
+ "@*.exception_handler",
23
+ "@*_required",
24
+ "@login_required",
25
+ "@permission_required",
26
+ "django.views.decorators.*",
27
+ "@*.simple_tag",
28
+ "@*.inclusion_tag",
29
+ "@validator",
30
+ "@field_validator",
31
+ "@model_validator",
32
+ "@root_validator",
33
+ "@field_serializer",
34
+ "@model_serializer",
35
+ "@computed_field",
36
+ ]
37
+
38
+ FRAMEWORK_FUNCTIONS = [
39
+ "get",
40
+ "post",
41
+ "put",
42
+ "patch",
43
+ "delete",
44
+ "head",
45
+ "options",
46
+ "trace",
47
+ "*_queryset",
48
+ "get_queryset",
49
+ "get_object",
50
+ "get_context_data",
51
+ "*_form",
52
+ "form_valid",
53
+ "form_invalid",
54
+ "get_form_*",
55
+ ]
56
+
57
+ FRAMEWORK_IMPORTS = {
58
+ "flask",
59
+ "fastapi",
60
+ "django",
61
+ "rest_framework",
62
+ "pydantic",
63
+ "celery",
64
+ "starlette",
65
+ "uvicorn",
66
+ }
67
+
68
+
69
+ class FrameworkAwareVisitor:
70
+ def __init__(self, filename=None):
71
+ self.is_framework_file = False
72
+ self.detected_frameworks = set()
73
+ self.framework_decorated_lines = set()
74
+ self.func_defs = {}
75
+ self.class_defs = {}
76
+ self.class_method_lines = {}
77
+ self.pydantic_models = set()
78
+ self._mark_functions = set()
79
+ self._mark_classes = set()
80
+ self.declarative_classes = set()
81
+ self._mark_cbv_http_methods = set()
82
+ self._type_refs_in_routes = set()
83
+ if filename:
84
+ self._check_framework_imports_in_file(filename)
85
+
86
+ def visit(self, node):
87
+ method = "visit_" + node.__class__.__name__
88
+ visitor = getattr(self, method, self.generic_visit)
89
+ return visitor(node)
90
+
91
+ def generic_visit(self, node):
92
+ for field, value in ast.iter_fields(node):
93
+ if isinstance(value, list):
94
+ for item in value:
95
+ if isinstance(item, ast.AST):
96
+ self.visit(item)
97
+ elif isinstance(value, ast.AST):
98
+ self.visit(value)
99
+
100
+ def visit_Import(self, node: ast.Import):
101
+ for alias in node.names:
102
+ name = alias.name.lower()
103
+
104
+ for fw in FRAMEWORK_IMPORTS:
105
+ if fw in name:
106
+ self.is_framework_file = True
107
+ framework_name = name.split(".")[0]
108
+ self.detected_frameworks.add(framework_name)
109
+ break
110
+
111
+ self.generic_visit(node)
112
+
113
+ def visit_ImportFrom(self, node: ast.ImportFrom):
114
+ if node.module:
115
+ module_name = node.module.split(".")[0].lower()
116
+ if module_name in FRAMEWORK_IMPORTS:
117
+ self.is_framework_file = True
118
+ self.detected_frameworks.add(module_name)
119
+ self.generic_visit(node)
120
+
121
+ def visit_FunctionDef(self, node: ast.FunctionDef):
122
+ self.func_defs.setdefault(node.name, node.lineno)
123
+ for deco in node.decorator_list:
124
+ d = self._normalize_decorator(deco)
125
+
126
+ if self._matches_framework_pattern(d, FRAMEWORK_DECORATORS):
127
+ self.framework_decorated_lines.add(node.lineno)
128
+ self.is_framework_file = True
129
+
130
+ if self._decorator_base_name_is(deco, "receiver"):
131
+ self.framework_decorated_lines.add(node.lineno)
132
+ self.is_framework_file = True
133
+
134
+ defaults_to_scan = []
135
+ if node.args.defaults:
136
+ defaults_to_scan.extend(node.args.defaults)
137
+ if node.args.kw_defaults:
138
+ defaults_to_scan.extend(node.args.kw_defaults)
139
+
140
+ for default in defaults_to_scan:
141
+ self._scan_for_depends(default)
142
+
143
+ is_route = False
144
+ if node.lineno in self.framework_decorated_lines:
145
+ is_route = True
146
+
147
+ if is_route:
148
+ self._collect_annotation_type_refs(node)
149
+ self.generic_visit(node)
150
+
151
+ visit_AsyncFunctionDef = visit_FunctionDef
152
+
153
+ def visit_ClassDef(self, node: ast.ClassDef):
154
+ self.class_defs[node.name] = node
155
+ for item in node.body:
156
+ if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
157
+ self.class_method_lines[(node.name, item.name)] = item.lineno
158
+ bases = self._base_names(node)
159
+
160
+ is_view_like = False
161
+ for base in bases:
162
+ for token in ("view", "viewset", "apiview", "handler"):
163
+ if token in base:
164
+ is_view_like = True
165
+ break
166
+ if is_view_like:
167
+ break
168
+
169
+ is_pydantic = False
170
+ for base in bases:
171
+ if "basemodel" in base:
172
+ is_pydantic = True
173
+ break
174
+
175
+ if is_view_like:
176
+ self.is_framework_file = True
177
+ self._mark_cbv_http_methods.add(node.name)
178
+
179
+ if is_pydantic:
180
+ self.pydantic_models.add(node.name)
181
+ self.declarative_classes.add(node.name)
182
+ self.is_framework_file = True
183
+
184
+ else:
185
+ for base in bases:
186
+ tail = base.split(".")[-1]
187
+ if tail in ("schema", "model"):
188
+ self.declarative_classes.add(node.name)
189
+ break
190
+
191
+ self.generic_visit(node)
192
+
193
+ def visit_Assign(self, node: ast.Assign):
194
+ targets = []
195
+ for t in node.targets:
196
+ if isinstance(t, ast.Name):
197
+ targets.append(t.id)
198
+
199
+ if "urlpatterns" in targets:
200
+ self.is_framework_file = True
201
+ for elt in self._iter_list_elts(node.value):
202
+ if isinstance(elt, ast.Call) and self._call_name_endswith(
203
+ elt, {"path", "re_path"}
204
+ ):
205
+ view_expr = self._get_posarg(elt, 1)
206
+ self._mark_view_from_url_pattern(view_expr)
207
+ self.generic_visit(node)
208
+
209
+ def visit_Call(self, node: ast.Call):
210
+ if isinstance(node.func, ast.Attribute) and node.func.attr == "register":
211
+ if len(node.args) >= 2:
212
+ vs = node.args[1]
213
+ cls_name = self._simple_name(vs)
214
+ if cls_name:
215
+ self._mark_classes.add(cls_name)
216
+ self._mark_cbv_http_methods.add(cls_name)
217
+ self.is_framework_file = True
218
+ if (
219
+ isinstance(node.func, ast.Attribute)
220
+ and node.func.attr == "connect"
221
+ and node.args
222
+ ):
223
+ func_name = self._simple_name(node.args[0])
224
+ if func_name:
225
+ self._mark_functions.add(func_name)
226
+ self.is_framework_file = True
227
+ self.generic_visit(node)
228
+
229
+ def finalize(self):
230
+ for fname in self._mark_functions:
231
+ if fname in self.func_defs:
232
+ self.framework_decorated_lines.add(self.func_defs[fname])
233
+ for cname in self._mark_classes:
234
+ cls_node = self.class_defs.get(cname)
235
+ if cls_node is not None:
236
+ self.framework_decorated_lines.add(cls_node.lineno)
237
+ for cname in self._mark_cbv_http_methods:
238
+ for meth in (
239
+ "get",
240
+ "post",
241
+ "put",
242
+ "patch",
243
+ "delete",
244
+ "head",
245
+ "options",
246
+ "trace",
247
+ "list",
248
+ "create",
249
+ "retrieve",
250
+ "update",
251
+ "partial_update",
252
+ "destroy",
253
+ ):
254
+ lino = self.class_method_lines.get((cname, meth))
255
+ if lino:
256
+ self.framework_decorated_lines.add(lino)
257
+
258
+ typed_models = set()
259
+ for t in self._type_refs_in_routes:
260
+ if t in self.pydantic_models:
261
+ typed_models.add(t)
262
+
263
+ self._mark_classes.update(typed_models)
264
+ for cname in typed_models:
265
+ cls_node = self.class_defs.get(cname)
266
+ if cls_node is not None:
267
+ self.framework_decorated_lines.add(cls_node.lineno)
268
+
269
+ def _check_framework_imports_in_file(self, filename):
270
+ try:
271
+ content = Path(filename).read_text(encoding="utf-8")
272
+
273
+ for framework in FRAMEWORK_IMPORTS:
274
+ import_statement = f"import {framework}"
275
+ from_statement = f"from {framework}"
276
+
277
+ has_import = import_statement in content
278
+ has_from_import = from_statement in content
279
+
280
+ if has_import or has_from_import:
281
+ self.is_framework_file = True
282
+ self.detected_frameworks.add(framework)
283
+ break
284
+
285
+ except Exception:
286
+ pass
287
+
288
+ def _normalize_decorator(self, dec: ast.AST):
289
+ if isinstance(dec, ast.Call):
290
+ return self._normalize_decorator(dec.func)
291
+ if isinstance(dec, ast.Name):
292
+ return f"@{dec.id}"
293
+ if isinstance(dec, ast.Attribute):
294
+ return f"@{self._attr_to_str(dec)}"
295
+ return "@unknown"
296
+
297
+ def _matches_framework_pattern(self, text, patterns):
298
+ text_clean = text.lstrip("@")
299
+
300
+ for pattern in patterns:
301
+ pattern_clean = pattern.lstrip("@")
302
+ if fnmatch.fnmatch(text_clean, pattern_clean):
303
+ return True
304
+
305
+ return False
306
+
307
+ def _decorator_base_name_is(self, dec: ast.AST, name):
308
+ if isinstance(dec, ast.Call):
309
+ dec = dec.func
310
+ if isinstance(dec, ast.Name):
311
+ return dec.id == name
312
+ if isinstance(dec, ast.Attribute):
313
+ return dec.attr == name or self._attr_to_str(dec).endswith("." + name)
314
+ return False
315
+
316
+ def _attr_to_str(self, node: ast.Attribute):
317
+ parts = []
318
+ cur = node
319
+ while isinstance(cur, ast.Attribute):
320
+ parts.append(cur.attr)
321
+ cur = cur.value
322
+ if isinstance(cur, ast.Name):
323
+ parts.append(cur.id)
324
+
325
+ parts.reverse()
326
+ return ".".join(parts)
327
+
328
+ def _base_names(self, node: ast.ClassDef):
329
+ out = []
330
+ for b in node.bases:
331
+ if isinstance(b, ast.Name):
332
+ out.append(b.id.lower())
333
+ elif isinstance(b, ast.Attribute):
334
+ out.append(self._attr_to_str(b).lower())
335
+ return out
336
+
337
+ def _iter_list_elts(self, node: ast.AST):
338
+ if isinstance(node, (ast.List, ast.Tuple, ast.Set)):
339
+ for elt in node.elts:
340
+ yield elt
341
+
342
+ def _call_name_endswith(self, call: ast.Call, names):
343
+ if isinstance(call.func, ast.Name):
344
+ return call.func.id in names
345
+ if isinstance(call.func, ast.Attribute):
346
+ return call.func.attr in names
347
+ return False
348
+
349
+ def _get_posarg(self, call: ast.Call, idx):
350
+ return call.args[idx] if len(call.args) > idx else None
351
+
352
+ def _simple_name(self, node: ast.AST):
353
+ if isinstance(node, ast.Name):
354
+ return node.id
355
+ if isinstance(node, ast.Attribute):
356
+ return node.attr
357
+ return None
358
+
359
+ def _mark_view_from_url_pattern(self, view_expr):
360
+ if view_expr is None:
361
+ return
362
+ if (
363
+ isinstance(view_expr, ast.Call)
364
+ and isinstance(view_expr.func, ast.Attribute)
365
+ and view_expr.func.attr == "as_view"
366
+ ):
367
+ cls_name = self._simple_name(view_expr.func.value)
368
+ if cls_name:
369
+ self._mark_classes.add(cls_name)
370
+ self._mark_cbv_http_methods.add(cls_name)
371
+ else:
372
+ fname = self._simple_name(view_expr)
373
+ if fname:
374
+ self._mark_functions.add(fname)
375
+
376
+ def _scan_for_depends(self, node):
377
+ if not isinstance(node, ast.Call):
378
+ return
379
+ is_depends = False
380
+ if isinstance(node.func, ast.Name) and node.func.id == "Depends":
381
+ is_depends = True
382
+ elif isinstance(node.func, ast.Attribute) and node.func.attr == "Depends":
383
+ is_depends = True
384
+ if not is_depends:
385
+ return
386
+ if node.args:
387
+ dep = node.args[0]
388
+ dep_name = self._simple_name(dep)
389
+ if dep_name:
390
+ self._mark_functions.add(dep_name)
391
+ self.is_framework_file = True
392
+
393
+ def _collect_annotation_type_refs(self, fn: ast.FunctionDef):
394
+ def collect(t):
395
+ if t is None:
396
+ return
397
+
398
+ if isinstance(t, ast.Name):
399
+ self._type_refs_in_routes.add(t.id)
400
+ return
401
+
402
+ if isinstance(t, ast.Attribute):
403
+ self._type_refs_in_routes.add(t.attr)
404
+ return
405
+
406
+ if isinstance(t, ast.Subscript):
407
+ collect(t.value)
408
+ slice_node = t.slice
409
+ if isinstance(slice_node, ast.Tuple):
410
+ for element in slice_node.elts:
411
+ collect(element)
412
+ else:
413
+ collect(slice_node)
414
+ return
415
+
416
+ if isinstance(t, ast.Tuple):
417
+ for element in t.elts:
418
+ collect(element)
419
+
420
+ all_args = []
421
+ all_args.extend(fn.args.args)
422
+ all_args.extend(fn.args.posonlyargs)
423
+ all_args.extend(fn.args.kwonlyargs)
424
+
425
+ for arg in all_args:
426
+ collect(arg.annotation)
427
+
428
+ if fn.returns:
429
+ collect(fn.returns)
430
+
431
+
432
+ def detect_framework_usage(definition, visitor=None):
433
+ if not visitor:
434
+ return None
435
+ if definition.line in visitor.framework_decorated_lines:
436
+ return 1
437
+ return None
@@ -0,0 +1,74 @@
1
+ import ast
2
+ from skylos.constants import TEST_IMPORT_RE, TEST_DECOR_RE, TEST_FILE_RE
3
+
4
+
5
+ class TestAwareVisitor:
6
+ def __init__(self, filename=None):
7
+ self.is_test_file = False
8
+ self.test_decorated_lines = set()
9
+
10
+ if filename and TEST_FILE_RE.search(str(filename)):
11
+ self.is_test_file = True
12
+
13
+ def visit(self, node):
14
+ method = "visit_" + node.__class__.__name__
15
+ visitor = getattr(self, method, self.generic_visit)
16
+ return visitor(node)
17
+
18
+ def generic_visit(self, node):
19
+ for field, value in ast.iter_fields(node):
20
+ if isinstance(value, list):
21
+ for item in value:
22
+ if isinstance(item, ast.AST):
23
+ self.visit(item)
24
+ elif isinstance(value, ast.AST):
25
+ self.visit(value)
26
+
27
+ def visit_Import(self, node):
28
+ if self.is_test_file:
29
+ for alias in node.names:
30
+ if TEST_IMPORT_RE.match(alias.name):
31
+ pass
32
+ self.generic_visit(node)
33
+
34
+ def visit_ImportFrom(self, node):
35
+ if self.is_test_file:
36
+ if node.module and TEST_IMPORT_RE.match(node.module):
37
+ pass
38
+ self.generic_visit(node)
39
+
40
+ def visit_FunctionDef(self, node):
41
+ if (
42
+ node.name.startswith("test_")
43
+ or node.name.endswith("_test")
44
+ or any(node.name.startswith(prefix) for prefix in ["setup", "teardown"])
45
+ or node.name
46
+ in [
47
+ "setUp",
48
+ "tearDown",
49
+ "setUpClass",
50
+ "tearDownClass",
51
+ "setUpModule",
52
+ "tearDownModule",
53
+ ]
54
+ ):
55
+ self.test_decorated_lines.add(node.lineno)
56
+
57
+ for deco in node.decorator_list:
58
+ name = self._decorator_name(deco)
59
+ if name and (
60
+ TEST_DECOR_RE.match(name) or "pytest" in name or "fixture" in name
61
+ ):
62
+ self.test_decorated_lines.add(node.lineno)
63
+ self.generic_visit(node)
64
+
65
+ def visit_AsyncFunctionDef(self, node):
66
+ self.visit_FunctionDef(node)
67
+
68
+ def _decorator_name(self, deco):
69
+ if isinstance(deco, ast.Name):
70
+ return deco.id
71
+ if isinstance(deco, ast.Attribute):
72
+ parent = self._decorator_name(deco.value)
73
+ return f"{parent}.{deco.attr}" if parent else deco.attr
74
+ return ""
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: skylos
3
+ Version: 2.5.2
4
+ Summary: An analysis tool for Python codebases
5
+ Author-email: oha <aaronoh2015@gmail.com>
6
+ Classifier: Development Status :: 4 - Beta
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: License :: OSI Approved :: Apache Software License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Classifier: Topic :: Software Development :: Testing
16
+ Requires-Python: >=3.9
17
+ Requires-Dist: inquirer>=3.0.0
18
+ Requires-Dist: flask>=2.1.1
19
+ Requires-Dist: flask-cors>=3.0.0
20
+ Requires-Dist: libcst>=1.8.2
21
+ Requires-Dist: rich>=14.0.0
@@ -0,0 +1,42 @@
1
+ skylos/__init__.py,sha256=LfCvFSFmNNylU9vOh4akThF09VoyWbLBQtHuu3sKToE,233
2
+ skylos/analyzer.py,sha256=K16xuj76d82HDzE15TRRXhqz-_3KQ3cknSQ3dupUViI,25448
3
+ skylos/cfg_visitor.py,sha256=ct5CGU4Is4logPGA_BCBKnapH-tYnGsCTdC9RRBHSDs,1741
4
+ skylos/cli.py,sha256=1IcsS0tUeTGC1KGsraNzpyCrWTUhoq2Sxzv9VzNRu_k,27615
5
+ skylos/codemods.py,sha256=XarxwgeVca3nQkEqR-VmYnD2T8dwlWZZv8DXipifmBM,9718
6
+ skylos/config.py,sha256=Y_sd8M63qR7t2Dj7K0rL0mhgKu_-4_5OOXoMT-N4PhQ,1055
7
+ skylos/constants.py,sha256=6Gy3NrnUAdiRd0o9OnRB0tZo1dh96bXGJrNxkN4FkQM,1623
8
+ skylos/gatekeeper.py,sha256=UaPNqDPAPZPYGy6ItCCWhTxoSrrsAeNQeQA-Xhx0h7U,5013
9
+ skylos/linter.py,sha256=jbaFON9eFjPWqjsgZXuchTO9q9VH4JRFdFtn03_QXmM,493
10
+ skylos/server.py,sha256=G0eBJmk4Yg1nu3mY0tL5bsDOUNg9hXh4ecZkOTiDcfc,15853
11
+ skylos/visitor.py,sha256=-QwNH1BbRQl-UVsRw6CUrynnQqFoyhcrWgzk6_w-lDI,21141
12
+ skylos/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ skylos/rules/base.py,sha256=a99wScVY-hnLK4wgtv0YuZ4Rnp93mgsQ5YHhsStdwv0,328
14
+ skylos/rules/secrets.py,sha256=prqQe1oIIgTiAynGJ626WU7F4Kiqnv1PsPCWsHcP934,9727
15
+ skylos/rules/danger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ skylos/rules/danger/calls.py,sha256=ImiaPsBb9VxCy6EuNBKwnb1LMk1ZeK-Z5m61fNeiVMg,3329
17
+ skylos/rules/danger/danger.py,sha256=x7TQZoQuUEd-kjqWJ3ORgFsjDwVARvqgqymar8RAqCQ,4360
18
+ skylos/rules/danger/taint.py,sha256=Hs8wvCk5Cb6htjaZ_p3DK_HtDXHzyHKwDc7WYxgX54Y,3343
19
+ skylos/rules/danger/danger_cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ skylos/rules/danger/danger_cmd/cmd_flow.py,sha256=KCQbIB1P9d2ym4hfmrqyf5LZpPk0WU_3qpmWIAY1N9M,2287
21
+ skylos/rules/danger/danger_fs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ skylos/rules/danger/danger_fs/path_flow.py,sha256=l47jK89qnDq0p5KPHrY_sQ8THpmDXfCUNKsjb-b0M2o,2462
23
+ skylos/rules/danger/danger_net/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ skylos/rules/danger/danger_net/ssrf_flow.py,sha256=E0YKXf-EqDs5a6FllmUYd7MG4LseSY0avsDXV0tG9MY,2639
25
+ skylos/rules/danger/danger_sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ skylos/rules/danger/danger_sql/sql_flow.py,sha256=YQ98ry-Jmg0gjbYuW00Iwj3oopFry0XNXPlmbOs_zD4,8336
27
+ skylos/rules/danger/danger_sql/sql_raw_flow.py,sha256=tYxUWmJv8yF9qrHWSyq2lfU70aLIG0D9RHpTWqg2VwQ,3117
28
+ skylos/rules/danger/danger_web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ skylos/rules/danger/danger_web/xss_flow.py,sha256=paNd1-54Kyz6XGsLloVU_Flr4ejg6HFDn4tJmwPOrjo,5667
30
+ skylos/rules/quality/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ skylos/rules/quality/complexity.py,sha256=RPEXF1kq5QVcR4kBge_MsOOFwjb326PZKe1scmBbaQs,2346
32
+ skylos/rules/quality/logic.py,sha256=TqSakQRjkt5crpFhpMKxzBxua6SexsjDHSPycEt2-zY,3612
33
+ skylos/rules/quality/nesting.py,sha256=rp7n513RMzgMRABxZFrfh-e4mOZXyGSk5H9qdUNurXk,3025
34
+ skylos/rules/quality/structure.py,sha256=HQt1l2G6s_Gb92qQC-ijLJbuTBmXxcbK1p_zQGUlf_o,2946
35
+ skylos/visitors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ skylos/visitors/framework_aware.py,sha256=O3uBy_1E2vMcJRX_cyi6W9KLY0e7c32sdHX63uSM7lA,13682
37
+ skylos/visitors/test_aware.py,sha256=eed7RJfo-8OE2TGYNmzqqgF8S5T25IjnlvcGkT_-BfU,2426
38
+ skylos-2.5.2.dist-info/METADATA,sha256=wvdyBcPqdMrrhpb54r3HOOj5KlVIkrX9eiZmA7TTw5E,824
39
+ skylos-2.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ skylos-2.5.2.dist-info/entry_points.txt,sha256=zzRpN2ByznlQoLeuLolS_TFNYSQxUGBL1EXQsAd6bIA,43
41
+ skylos-2.5.2.dist-info/top_level.txt,sha256=GHAM9ZPjXcmflVoLHDN1wMtwoUBFZ3I0AYKOXvfHv70,7
42
+ skylos-2.5.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: skylos
3
- Version: 1.0.10
4
- Summary: A static analysis tool for Python codebases
5
- Author-email: oha <aaronoh2015@gmail.com>
6
- Requires-Python: >=3.9
7
- Requires-Dist: inquirer>=3.0.0
8
- Dynamic: requires-python
@@ -1,21 +0,0 @@
1
- skylos/__init__.py,sha256=U8NngtBsmaUGNLstSCYzDjfSCT1KyFOKExYRX7VmZfw,152
2
- skylos/analyzer.py,sha256=1joG0Ek3jNj7qv7w3gFiNeb2BD8CXgX5PF6fVc_HIG4,9710
3
- skylos/cli.py,sha256=l-qfaC0RUH2L9YgjlMOvlQrCPD5hcV3McHIk1az-CI4,13525
4
- skylos/visitor.py,sha256=uHNHKf7Kf8Qg1sIa-PsH2NHCQD6R9Bd_NELs-41deE8,9339
5
- test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- test/compare_tools.py,sha256=0g9PDeJlbst-7hOaQzrL4MiJFQKpqM8q8VeBGzpPczg,22738
7
- test/diagnostics.py,sha256=ExuFOCVpc9BDwNYapU96vj9RXLqxji32Sv6wVF4nJYU,13802
8
- test/test_skylos.py,sha256=kz77STrS4k3Eez5RDYwGxOg2WH3e7zNZPUYEaTLbGTs,15608
9
- test/test_visitor.py,sha256=bxUY_Zn_gLadZlz_n3Mu6rhVcExqElISwwVBo4eqVAY,7337
10
- test/sample_repo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- test/sample_repo/app.py,sha256=M5XgoAn-LPz50mKAj_ZacRKf-Pg7I4HbjWP7Z9jE4a0,226
12
- test/sample_repo/sample_repo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- test/sample_repo/sample_repo/commands.py,sha256=b6gQ9YDabt2yyfqGbOpLo0osF7wya8O4Lm7m8gtCr3g,2575
14
- test/sample_repo/sample_repo/models.py,sha256=xXIg3pToEZwKuUCmKX2vTlCF_VeFA0yZlvlBVPIy5Qw,3320
15
- test/sample_repo/sample_repo/routes.py,sha256=8yITrt55BwS01G7nWdESdx8LuxmReqop1zrGUKPeLi8,2475
16
- test/sample_repo/sample_repo/utils.py,sha256=S56hEYh8wkzwsD260MvQcmUFOkw2EjFU27nMLFE6G2k,1103
17
- skylos-1.0.10.dist-info/METADATA,sha256=iVIhmRsXWo0WPlMjYVrZpsi2mUVdEbKYrXDvOW4hbIk,225
18
- skylos-1.0.10.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
19
- skylos-1.0.10.dist-info/entry_points.txt,sha256=zzRpN2ByznlQoLeuLolS_TFNYSQxUGBL1EXQsAd6bIA,43
20
- skylos-1.0.10.dist-info/top_level.txt,sha256=f8GA_7KwfaEopPMP8-EXDQXaqd4IbsOQPakZy01LkdQ,12
21
- skylos-1.0.10.dist-info/RECORD,,