skylos 2.2.3__tar.gz → 2.4.0__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.

Potentially problematic release.


This version of skylos might be problematic. Click here for more details.

Files changed (64) hide show
  1. {skylos-2.2.3 → skylos-2.4.0}/PKG-INFO +1 -1
  2. {skylos-2.2.3 → skylos-2.4.0}/README.md +150 -81
  3. {skylos-2.2.3 → skylos-2.4.0}/pyproject.toml +1 -1
  4. {skylos-2.2.3 → skylos-2.4.0}/setup.py +1 -1
  5. {skylos-2.2.3 → skylos-2.4.0}/skylos/__init__.py +1 -1
  6. {skylos-2.2.3 → skylos-2.4.0}/skylos/analyzer.py +119 -67
  7. {skylos-2.2.3 → skylos-2.4.0}/skylos/cli.py +172 -10
  8. skylos-2.4.0/skylos/rules/danger/danger.py +141 -0
  9. skylos-2.4.0/skylos/rules/danger/danger_cmd/cmd_flow.py +208 -0
  10. skylos-2.4.0/skylos/rules/danger/danger_fs/path_flow.py +188 -0
  11. skylos-2.4.0/skylos/rules/danger/danger_net/ssrf_flow.py +198 -0
  12. skylos-2.4.0/skylos/rules/danger/danger_sql/__init__.py +0 -0
  13. skylos-2.4.0/skylos/rules/danger/danger_sql/sql_flow.py +175 -0
  14. skylos-2.4.0/skylos/rules/danger/danger_sql/sql_raw_flow.py +202 -0
  15. skylos-2.4.0/skylos/rules/danger/danger_web/__init__.py +0 -0
  16. skylos-2.4.0/skylos/rules/danger/danger_web/xss_flow.py +279 -0
  17. {skylos-2.2.3 → skylos-2.4.0}/skylos/rules/secrets.py +34 -5
  18. skylos-2.4.0/skylos/visitors/__init__.py +0 -0
  19. {skylos-2.2.3 → skylos-2.4.0}/skylos.egg-info/PKG-INFO +1 -1
  20. {skylos-2.2.3 → skylos-2.4.0}/skylos.egg-info/SOURCES.txt +18 -0
  21. skylos-2.4.0/test/__init__.py +0 -0
  22. skylos-2.4.0/test/sample_repo/__init__.py +0 -0
  23. skylos-2.4.0/test/sample_repo/sample_repo/__init__.py +0 -0
  24. skylos-2.4.0/test/test_cmd_injection.py +41 -0
  25. skylos-2.4.0/test/test_dangerous.py +101 -0
  26. skylos-2.4.0/test/test_path_traversal.py +40 -0
  27. {skylos-2.2.3 → skylos-2.4.0}/test/test_secrets.py +24 -10
  28. skylos-2.4.0/test/test_sql_injection.py +54 -0
  29. skylos-2.4.0/test/test_ssrf.py +51 -0
  30. {skylos-2.2.3 → skylos-2.4.0}/setup.cfg +0 -0
  31. {skylos-2.2.3 → skylos-2.4.0}/skylos/codemods.py +0 -0
  32. {skylos-2.2.3 → skylos-2.4.0}/skylos/constants.py +0 -0
  33. {skylos-2.2.3 → skylos-2.4.0}/skylos/rules/__init__.py +0 -0
  34. {skylos-2.2.3/skylos/visitors → skylos-2.4.0/skylos/rules/danger}/__init__.py +0 -0
  35. {skylos-2.2.3/test → skylos-2.4.0/skylos/rules/danger/danger_cmd}/__init__.py +0 -0
  36. {skylos-2.2.3/test/sample_repo → skylos-2.4.0/skylos/rules/danger/danger_fs}/__init__.py +0 -0
  37. {skylos-2.2.3/test/sample_repo/sample_repo → skylos-2.4.0/skylos/rules/danger/danger_net}/__init__.py +0 -0
  38. {skylos-2.2.3 → skylos-2.4.0}/skylos/server.py +0 -0
  39. {skylos-2.2.3 → skylos-2.4.0}/skylos/visitor.py +0 -0
  40. {skylos-2.2.3 → skylos-2.4.0}/skylos/visitors/framework_aware.py +0 -0
  41. {skylos-2.2.3 → skylos-2.4.0}/skylos/visitors/test_aware.py +0 -0
  42. {skylos-2.2.3 → skylos-2.4.0}/skylos.egg-info/dependency_links.txt +0 -0
  43. {skylos-2.2.3 → skylos-2.4.0}/skylos.egg-info/entry_points.txt +0 -0
  44. {skylos-2.2.3 → skylos-2.4.0}/skylos.egg-info/requires.txt +0 -0
  45. {skylos-2.2.3 → skylos-2.4.0}/skylos.egg-info/top_level.txt +0 -0
  46. {skylos-2.2.3 → skylos-2.4.0}/test/compare_tools.py +0 -0
  47. {skylos-2.2.3 → skylos-2.4.0}/test/conftest.py +0 -0
  48. {skylos-2.2.3 → skylos-2.4.0}/test/diagnostics.py +0 -0
  49. {skylos-2.2.3 → skylos-2.4.0}/test/sample_repo/app.py +0 -0
  50. {skylos-2.2.3 → skylos-2.4.0}/test/sample_repo/sample_repo/commands.py +0 -0
  51. {skylos-2.2.3 → skylos-2.4.0}/test/sample_repo/sample_repo/models.py +0 -0
  52. {skylos-2.2.3 → skylos-2.4.0}/test/sample_repo/sample_repo/routes.py +0 -0
  53. {skylos-2.2.3 → skylos-2.4.0}/test/sample_repo/sample_repo/utils.py +0 -0
  54. {skylos-2.2.3 → skylos-2.4.0}/test/test_analyzer.py +0 -0
  55. {skylos-2.2.3 → skylos-2.4.0}/test/test_changes_analyzer.py +0 -0
  56. {skylos-2.2.3 → skylos-2.4.0}/test/test_cli.py +0 -0
  57. {skylos-2.2.3 → skylos-2.4.0}/test/test_codemods.py +0 -0
  58. {skylos-2.2.3 → skylos-2.4.0}/test/test_constants.py +0 -0
  59. {skylos-2.2.3 → skylos-2.4.0}/test/test_framework_aware.py +0 -0
  60. {skylos-2.2.3 → skylos-2.4.0}/test/test_integration.py +0 -0
  61. {skylos-2.2.3 → skylos-2.4.0}/test/test_new_behaviours.py +0 -0
  62. {skylos-2.2.3 → skylos-2.4.0}/test/test_skylos.py +0 -0
  63. {skylos-2.2.3 → skylos-2.4.0}/test/test_test_aware.py +0 -0
  64. {skylos-2.2.3 → skylos-2.4.0}/test/test_visitor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skylos
3
- Version: 2.2.3
3
+ Version: 2.4.0
4
4
  Summary: A static analysis tool for Python codebases
5
5
  Author-email: oha <aaronoh2015@gmail.com>
6
6
  Requires-Python: >=3.9
@@ -2,8 +2,11 @@
2
2
 
3
3
  ![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)
4
4
  ![100% Local](https://img.shields.io/badge/privacy-100%25%20local-brightgreen)
5
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/skylos)
5
6
  ![PyPI version](https://img.shields.io/pypi/v/skylos)
7
+ ![VS Code Marketplace](https://img.shields.io/visual-studio-marketplace/v/oha.skylos-vscode-extension)
6
8
  ![Security Policy](https://img.shields.io/badge/security-policy-brightgreen)
9
+ ![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)
7
10
 
8
11
  <div align="center">
9
12
  <img src="assets/SKYLOS.png" alt="Skylos Logo" width="200">
@@ -25,7 +28,7 @@
25
28
  - [Web Interface](#web-interface)
26
29
  - [Design](#design)
27
30
  - [Test File Detection](#test-file-detection)
28
- - [Folder Management](#folder-management)
31
+ - [Vibe Coding](#vibe-coding)
29
32
  - [Ignoring Pragmas](#ignoring-pragmas)
30
33
  - [Including & Excluding Files](#including--excluding-files)
31
34
  - [CLI Options](#cli-options)
@@ -33,6 +36,7 @@
33
36
  - [Interactive Mode](#interactive-mode)
34
37
  - [Development](#development)
35
38
  - [CI/CD (Pre-commit & GitHub Actions)](#cicd-pre-commit--github-actions)
39
+ - [VS Code extension](#vsc-extension)
36
40
  - [FAQ](#faq)
37
41
  - [Limitations](#limitations)
38
42
  - [Troubleshooting](#troubleshooting)
@@ -52,8 +56,8 @@
52
56
  * **Unused Imports**: Identifies imports that are not used
53
57
  * **Folder Management**: Inclusion/exclusion of directories
54
58
  * **Ignore Pragmas**: Skip lines tagged with `# pragma: no skylos`, `# pragma: no cover`, or `# noqa`
55
- **NEW** **Secrets Scanning (PoC, opt-in)**: Detects API keys & secrets (GitHub, GitLab, Slack, Stripe, AWS, Google, SendGrid, Twilio, private key blocks)
56
-
59
+ * **NEW** **Secrets Scanning (PoC, opt-in)**: Detects API keys & secrets (GitHub, GitLab, Slack, Stripe, AWS, Google, SendGrid, Twilio, private key blocks)
60
+ * **NEW** **Dangerous Patterns**: Flags risky code such as `eval/exec`, `os.system`, `subprocess(shell=True)`, `pickle.load/loads`, `yaml.load` without SafeLoader, hashlib.md5/sha1. Refer to `DANGEROUS_CODE.md` for the whole list. This includes SQL injection, path traversal and any other security flaws that may arise from the practise of vibe-coding.
57
61
 
58
62
  ## Benchmark (You can find this benchmark test in `test` folder)
59
63
 
@@ -98,6 +102,7 @@ pip install .
98
102
  skylos /path/to/your/project
99
103
 
100
104
  skylos /path/to/your/project --secrets ## include api key scan
105
+ skylos /path/to/your/project --danger ## include safety scan for dangerous code
101
106
 
102
107
  # To launch the front end
103
108
  skylos run
@@ -111,8 +116,12 @@ skylos . --interactive --comment-out
111
116
  # Dry run - see what would be removed
112
117
  skylos --interactive --dry-run /path/to/your/project
113
118
 
119
+ # Load the results in json format
114
120
  skylos --json /path/to/your/project
115
121
 
122
+ # Load the results in table format
123
+ skylos --table /path/to/your/project
124
+
116
125
  # With confidence
117
126
  skylos path/to/your/file --confidence 20 ## or whatever value u wanna set
118
127
  ```
@@ -199,6 +208,49 @@ When Skylos detects a test file, it by default, will apply a confidence penalty
199
208
  /project/test_data.py
200
209
  ```
201
210
 
211
+ ## Vibe-Coding
212
+
213
+ We are aware that vibe coding has created a lot of vulnerabilities. To an AI, it's job is to spit out a list of tokens with the highest probability, whether it's right or not. This may introduce vulnerabilities in their applications. We have expanded Skylos to catch the most important problems first.
214
+
215
+ - SQL injection (cursor)
216
+ - SQL injection (raw-API)
217
+ - Command injection
218
+ - SSRF
219
+ - Path traversal
220
+ - eval/exec
221
+ - pickle.load/loads
222
+ - yaml.load w/o SafeLoader
223
+ - weak hashes MD5/SHA1
224
+ - subprocess(..., shell=True)
225
+ - requests(..., verify=False)
226
+
227
+ ### Quick Start
228
+
229
+ ```bash
230
+ skylos /path/to/your/project --danger
231
+ ```
232
+
233
+ ### Examples that will be flagged
234
+
235
+ ```
236
+ # SQLi (cursor)
237
+ cur.execute(f"SELECT * FROM users WHERE name='{name}'")
238
+
239
+ # SQLi (raw-api)
240
+ pd.read_sql("SELECT * FROM users WHERE name='" + q + "'", conn)
241
+
242
+ # Command injection
243
+ os.system("zip -r out.zip " + folder)
244
+
245
+ # SSRF
246
+ requests.get(request.args["url"], timeout=3)
247
+
248
+ # Path traversal
249
+ with open(request.args.get("p"), "r") as f: ...
250
+ ```
251
+
252
+ This list will be expanded in the near future. For more information, refer to `DANGEROUS_CODE.md`
253
+
202
254
  ## Ignoring Pragmas
203
255
 
204
256
  1. To ignore any warning, indicate `# pragma: no skylos` **ON THE SAME LINE** as the function/class you want to ignore
@@ -251,6 +303,7 @@ Arguments:
251
303
  Options:
252
304
  -h, --help Show this help message and exit
253
305
  --json Output raw JSON instead of formatted text
306
+ --table Output results in table format via the CLI
254
307
  -o, --output FILE Write output to file instead of stdout
255
308
  -v, --verbose Enable verbose output
256
309
  --version Checks version
@@ -262,6 +315,7 @@ Options:
262
315
  --list-default-excludes List the default excluded folders and
263
316
  -c, --confidence LEVEL Confidence threshold (0-100). Lower values will show more items.
264
317
  -- secrets Scan for api keys/secrets
318
+ -- danger Scan for dangerous code
265
319
  ```
266
320
 
267
321
  ## Interactive Mode
@@ -276,22 +330,97 @@ The interactive mode lets you select specific functions and imports to remove:
276
330
 
277
331
  Pick **one** (or use **both**)
278
332
 
279
- 1. Pre-commit (local + CI): runs Skylos before commits/PRs.
333
+ 1. GitHub Actions: runs Skylos on pushes/PRs in CI.
334
+ - No local install needed
335
+
336
+ 2. Pre-commit (local + CI): runs Skylos before commits/PRs.
280
337
  - You must install pre-commit locally once. Skylos gets installed automatically by the hook.
281
338
 
282
- 2. GitHub Actions: runs Skylos on pushes/PRs in CI.
283
- - No local install needed
339
+ ### Option A Github Actions
340
+
341
+ 1. Create .github/workflows/skylos.yml **(COPY THE ENTIRE SKYLOS.YAML FROM BELOW)**:
342
+
343
+ ```yaml
344
+ name: Skylos Deadcode Scan
345
+
346
+ on:
347
+ pull_request:
348
+ push:
349
+ branches: [ main, master ]
350
+ workflow_dispatch:
351
+
352
+ jobs:
353
+ scan:
354
+ runs-on: ubuntu-latest
355
+ env:
356
+ SKYLOS_STRICT: ${{ vars.SKYLOS_STRICT || 'false' }}
357
+ steps:
358
+ - uses: actions/checkout@v4
359
+
360
+ - uses: actions/setup-python@v5
361
+ with:
362
+ python-version: '3.11'
363
+ cache: 'pip'
284
364
 
285
- ### Option A — Pre-commit (local + CI)
365
+ - name: Install Skylos
366
+ run: pip install skylos
286
367
 
287
- 1. Create or edit `.pre-commit-config.yaml` at the repo root:
368
+ - name: Run Skylos
369
+ env:
370
+ REPORT: skylos_${{ github.run_number }}_${{ github.sha }}.json
371
+ run: |
372
+ echo "REPORT=$REPORT" >> "$GITHUB_OUTPUT"
373
+ skylos . --json > "$REPORT"
374
+ id: scan
375
+
376
+ - name: Fail if there are findings
377
+ continue-on-error: ${{ env.SKYLOS_STRICT != 'true' }}
378
+ env:
379
+ REPORT: ${{ steps.scan.outputs.REPORT }}
380
+ run: |
381
+ python - << 'PY'
382
+ import json, sys, os
383
+ report = os.environ["REPORT"]
384
+ data = json.load(open(report, "r", encoding="utf-8"))
385
+ count = 0
386
+ for value in data.values():
387
+ if isinstance(value, list):
388
+ count += len(value)
389
+ print(f"Findings: {count}")
390
+ if count > 0:
391
+ print(f"::warning title=Skylos findings::{count} potential issues found. See {report}")
392
+ sys.exit(1 if count > 0 else 0)
393
+ PY
394
+
395
+ - name: Upload report artifact
396
+ if: always()
397
+ uses: actions/upload-artifact@v4
398
+ with:
399
+ name: ${{ steps.scan.outputs.REPORT }}
400
+ path: ${{ steps.scan.outputs.REPORT }}
401
+
402
+ - name: Summarize in job log
403
+ if: always()
404
+ run: |
405
+ echo "Skylos report: ${{ steps.scan.outputs.REPORT }}" >> $GITHUB_STEP_SUMMARY
406
+ ```
407
+
408
+ **To make the job fail on findings (strict mode)**:
409
+
410
+ 1. Go to GitHub -> Settings -> Secrets and variables -> Actions -> Variables
411
+
412
+ 2. Add variable SKYLOS_STRICT with value true
413
+
414
+ ### Option B — Pre-commit (local + CI)
415
+
416
+ . Create or edit `.pre-commit-config.yaml` at the repo root:
288
417
 
289
418
  **A: Skylos hook repo**
290
419
  ```yaml
291
420
  ## .pre-commit-config.yaml
292
421
  repos:
293
422
  - repo: https://github.com/duriantaco/skylos
294
- rev: v2.2.3
423
+ rev: v2.4.0
295
424
  hooks:
296
425
  - id: skylos-scan
297
426
  name: skylos report
@@ -300,7 +429,7 @@ repos:
300
429
  types_or: [python]
301
430
  pass_filenames: false
302
431
  require_serial: true
303
- args: [".", "--output", "report.json", "--confidence", "70"]
432
+ args: [".", "--output", "report.json", "--confidence", "70", "--danger"]
304
433
 
305
434
  - repo: local
306
435
  hooks:
@@ -341,7 +470,7 @@ repos:
341
470
  entry: python -m skylos.cli
342
471
  pass_filenames: false
343
472
  require_serial: true
344
- additional_dependencies: [skylos==2.2.3]
473
+ additional_dependencies: [skylos==2.4.0]
345
474
  args: [".", "--output", "report.json", "--confidence", "70"]
346
475
 
347
476
  - id: skylos-fail-on-findings
@@ -397,80 +526,15 @@ jobs:
397
526
 
398
527
  **Pre commit behavior:** the second hook is soft by default (SKYLOS_SOFT=1). This means that it prints findings and passes. You can remove the env/logic if you want pre-commit to block commits on finding
399
528
 
400
- ### Option B — Github Actions
529
+ ## VS Code extension
401
530
 
402
- 1. Create .github/workflows/skylos.yml:
531
+ Skylos has a VS Code extension that runs on save like a linter. Runs automatically on save of Python files. You will ll see squiggles + a popup like "Skylos found N items." For more information, refer to the VSC `README.md` inside the marketplace.
403
532
 
404
- ```yaml
405
- name: Skylos Deadcode Scan
533
+ ### Install
406
534
 
407
- on:
408
- pull_request:
409
- push:
410
- branches: [ main, master ]
411
- workflow_dispatch:
535
+ From VS Code Marketplace: "Skylos" (publisher: oha)
412
536
 
413
- jobs:
414
- scan:
415
- runs-on: ubuntu-latest
416
- env:
417
- SKYLOS_STRICT: ${{ vars.SKYLOS_STRICT || 'false' }}
418
- steps:
419
- - uses: actions/checkout@v4
420
-
421
- - uses: actions/setup-python@v5
422
- with:
423
- python-version: '3.11'
424
- cache: 'pip'
425
-
426
- - name: Install Skylos
427
- run: pip install skylos
428
-
429
- - name: Run Skylos
430
- env:
431
- REPORT: skylos_${{ github.run_number }}_${{ github.sha }}.json
432
- run: |
433
- echo "REPORT=$REPORT" >> "$GITHUB_OUTPUT"
434
- skylos . --json > "$REPORT"
435
- id: scan
436
-
437
- - name: Fail if there are findings
438
- continue-on-error: ${{ env.SKYLOS_STRICT != 'true' }}
439
- env:
440
- REPORT: ${{ steps.scan.outputs.REPORT }}
441
- run: |
442
- python - << 'PY'
443
- import json, sys, os
444
- report = os.environ["REPORT"]
445
- data = json.load(open(report, "r", encoding="utf-8"))
446
- count = 0
447
- for value in data.values():
448
- if isinstance(value, list):
449
- count += len(value)
450
- print(f"Findings: {count}")
451
- if count > 0:
452
- print(f"::warning title=Skylos findings::{count} potential issues found. See {report}")
453
- sys.exit(1 if count > 0 else 0)
454
- PY
455
-
456
- - name: Upload report artifact
457
- if: always()
458
- uses: actions/upload-artifact@v4
459
- with:
460
- name: ${{ steps.scan.outputs.REPORT }}
461
- path: ${{ steps.scan.outputs.REPORT }}
462
-
463
- - name: Summarize in job log
464
- if: always()
465
- run: |
466
- echo "Skylos report: ${{ steps.scan.outputs.REPORT }}" >> $GITHUB_STEP_SUMMARY
467
- ```
468
-
469
- **To make the job fail on findings (strict mode)**:
470
-
471
- 1. Go to GitHub -> Settings -> Secrets and variables -> Actions -> Variables
472
-
473
- 2. Add variable SKYLOS_STRICT with value true
537
+ Version: `0.1.0`
474
538
 
475
539
  ## Development
476
540
 
@@ -519,6 +583,9 @@ A: Web framework routes are given low confidence (20) because they might be call
519
583
  **Q: What confidence level should I use?**
520
584
  A: Start with 60 (default) for safe cleanup. Use 30 for framework applications. Use 20 for more comprehensive auditing.
521
585
 
586
+ **Q: What does `--danger` check**?
587
+ A: It flags common security problems. Refer to `DANGEROUS_CODE.md` for the full details
588
+
522
589
  ## Limitations
523
590
 
524
591
  - **Dynamic code**: `getattr()`, `globals()`, runtime imports are hard to detect
@@ -562,6 +629,8 @@ We welcome contributions! Please read our [Contributing Guidelines](CONTRIBUTING
562
629
  - [x] CI/CD integration examples
563
630
  - [ ] Further optimization
564
631
  - [ ] Add new rules
632
+ - [ ] Expanding on the `dangerous.py` list
633
+ - [ ] Porting to uv
565
634
 
566
635
  ## License
567
636
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "skylos"
7
- version = "2.2.3"
7
+ version = "2.4.0"
8
8
  requires-python = ">=3.9"
9
9
  description = "A static analysis tool for Python codebases"
10
10
  authors = [{name = "oha", email = "aaronoh2015@gmail.com"}]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="skylos",
5
- version="2.2.3",
5
+ version="2.4.0",
6
6
  packages=find_packages(),
7
7
  python_requires=">=3.9",
8
8
  install_requires=[
@@ -1,4 +1,4 @@
1
- __version__ = "2.2.3"
1
+ __version__ = "2.4.0"
2
2
 
3
3
  def analyze(*args, **kwargs):
4
4
  from .analyzer import analyze as _analyze
@@ -9,6 +9,7 @@ from skylos.visitor import Visitor
9
9
  from skylos.constants import ( PENALTIES, AUTO_CALLED )
10
10
  from skylos.visitors.test_aware import TestAwareVisitor
11
11
  from skylos.rules.secrets import scan_ctx as _secrets_scan_ctx
12
+ from skylos.rules.danger.danger import scan_ctx as scan_danger
12
13
  import os
13
14
  import traceback
14
15
  from skylos.visitors.framework_aware import FrameworkAwareVisitor, detect_framework_usage
@@ -238,7 +239,7 @@ class Skylos:
238
239
  if method.simple_name == "format" and cls.endswith("Formatter"):
239
240
  method.references += 1
240
241
 
241
- def analyze(self, path, thr=60, exclude_folders= None, enable_secrets = False):
242
+ def analyze(self, path, thr=60, exclude_folders= None, enable_secrets = False, enable_danger = False):
242
243
  files, root = self._get_python_files(path, exclude_folders)
243
244
 
244
245
  if not files:
@@ -262,6 +263,7 @@ class Skylos:
262
263
  modmap[f] = self._module(root, f)
263
264
 
264
265
  all_secrets = []
266
+ all_dangers = []
265
267
  for file in files:
266
268
  mod = modmap[file]
267
269
  defs, refs, dyn, exports, test_flags, framework_flags = proc_file(file, mod)
@@ -276,13 +278,25 @@ class Skylos:
276
278
 
277
279
  if enable_secrets and _secrets_scan_ctx is not None:
278
280
  try:
279
- src_lines = Path(file).read_text(encoding="utf-8", errors="ignore").splitlines(True)
280
- ctx = {"relpath": str(file), "lines": src_lines, "tree": None}
281
+ src = Path(file).read_text(encoding="utf-8", errors="ignore")
282
+ src_lines = src.splitlines(True)
283
+ rel = str(Path(file).relative_to(root))
284
+ ctx = {"relpath": rel, "lines": src_lines, "tree": None}
281
285
  findings = list(_secrets_scan_ctx(ctx))
282
286
  if findings:
283
287
  all_secrets.extend(findings)
284
288
  except Exception:
285
289
  pass
290
+
291
+ if enable_danger and scan_danger is not None:
292
+ try:
293
+ findings = scan_danger(root, [file])
294
+ if findings:
295
+ all_dangers.extend(findings)
296
+ except Exception as e:
297
+ logger.error(f"Error scanning {file} for dangerous code: {e}")
298
+ if os.getenv("SKYLOS_DEBUG"):
299
+ logger.error(traceback.format_exc())
286
300
 
287
301
  self._mark_refs()
288
302
  self._apply_heuristics()
@@ -296,7 +310,6 @@ class Skylos:
296
310
  for d in sorted(self.defs.values(), key=def_sort_key):
297
311
  if shown >= 50:
298
312
  break
299
- print(f" type={d.type} refs={d.references} conf={d.confidence} exported={d.is_exported} line={d.line} name={d.name}")
300
313
  shown += 1
301
314
 
302
315
  unused = []
@@ -318,7 +331,12 @@ class Skylos:
318
331
 
319
332
  if enable_secrets and all_secrets:
320
333
  result["secrets"] = all_secrets
334
+ result["analysis_summary"]["secrets_count"] = len(all_secrets)
321
335
 
336
+ if enable_danger and all_dangers:
337
+ result["danger"] = all_dangers
338
+ result["analysis_summary"]["danger_count"] = len(all_dangers)
339
+
322
340
  for u in unused:
323
341
  if u["type"] in ("function", "method"):
324
342
  result["unused_functions"].append(u)
@@ -370,70 +388,104 @@ def proc_file(file_or_args, mod=None):
370
388
 
371
389
  return [], [], set(), set(), dummy_visitor, dummy_framework_visitor
372
390
 
373
- def analyze(path, conf=60, exclude_folders=None, enable_secrets=False):
374
- return Skylos().analyze(path,conf, exclude_folders, enable_secrets)
391
+ def analyze(path, conf=60, exclude_folders=None, enable_secrets=False, enable_danger=False):
392
+ return Skylos().analyze(path,conf, exclude_folders, enable_secrets, enable_danger)
375
393
 
376
394
  if __name__ == "__main__":
377
- if len(sys.argv)>1:
378
- p = sys.argv[1]
379
- confidence = int(sys.argv[2]) if len(sys.argv) >2 else 60
380
- result = analyze(p,confidence)
381
-
382
- data = json.loads(result)
383
- print("\n Python Static Analysis Results")
384
- print("===================================\n")
385
-
386
- total_items = 0
387
- for key, items in data.items():
388
- if key.startswith("unused_") and isinstance(items, list):
389
- total_items += len(items)
390
-
391
- print("Summary:")
392
- if data["unused_functions"]:
393
- print(f" * Unreachable functions: {len(data['unused_functions'])}")
394
- if data["unused_imports"]:
395
- print(f" * Unused imports: {len(data['unused_imports'])}")
396
- if data["unused_classes"]:
397
- print(f" * Unused classes: {len(data['unused_classes'])}")
398
- if data["unused_variables"]:
399
- print(f" * Unused variables: {len(data['unused_variables'])}")
400
-
401
- if data["unused_functions"]:
402
- print("\n - Unreachable Functions")
403
- print("=======================")
404
- for i, func in enumerate(data["unused_functions"], 1):
405
- print(f" {i}. {func['name']}")
406
- print(f" └─ {func['file']}:{func['line']}")
407
-
408
- if data["unused_imports"]:
409
- print("\n - Unused Imports")
410
- print("================")
411
- for i, imp in enumerate(data["unused_imports"], 1):
412
- print(f" {i}. {imp['simple_name']}")
413
- print(f" └─ {imp['file']}:{imp['line']}")
414
-
415
- if data["unused_classes"]:
416
- print("\n - Unused Classes")
417
- print("=================")
418
- for i, cls in enumerate(data["unused_classes"], 1):
419
- print(f" {i}. {cls['name']}")
420
- print(f" └─ {cls['file']}:{cls['line']}")
421
-
422
- if data["unused_variables"]:
423
- print("\n - Unused Variables")
424
- print("==================")
425
- for i, var in enumerate(data["unused_variables"], 1):
426
- print(f" {i}. {var['name']}")
427
- print(f" └─ {var['file']}:{var['line']}")
428
-
429
- print("\n" + "─" * 50)
430
- print(f"Found {total_items} dead code items. Add this badge to your README:")
431
- print(f"```markdown")
432
- print(f"![Dead Code: {total_items}](https://img.shields.io/badge/Dead_Code-{total_items}_detected-orange?logo=codacy&logoColor=red)")
433
- print(f"```")
395
+ enable_secrets = ("--secrets" in sys.argv)
396
+ enable_danger = ("--danger" in sys.argv)
397
+
398
+ positional = [a for a in sys.argv[1:] if not a.startswith("--")]
399
+
400
+ if not positional:
401
+ print("Usage: python Skylos.py <path> [confidence_threshold] [--secrets] [--danger]")
402
+ sys.exit(2)
403
+
404
+ p = positional[0]
405
+ confidence = int(positional[1]) if len(positional) > 1 else 60
406
+
407
+ result = analyze(p, confidence, enable_secrets=enable_secrets, enable_danger=enable_danger)
434
408
 
435
- print("\nNext steps:")
436
- print(" * Use --interactive to select specific items to remove")
437
- print(" * Use --dry-run to preview changes before applying them")
409
+ data = json.loads(result)
410
+ print("\n Python Static Analysis Results")
411
+ print("===================================\n")
412
+
413
+ total_dead = 0
414
+ for key, items in data.items():
415
+ if key.startswith("unused_") and isinstance(items, list):
416
+ total_dead += len(items)
417
+
418
+ danger_count = data.get("analysis_summary", {}).get("danger_count", 0) if enable_danger else 0
419
+ secrets_count = data.get("analysis_summary", {}).get("secrets_count", 0) if enable_secrets else 0
420
+
421
+ print("Summary:")
422
+ if data["unused_functions"]:
423
+ print(f" * Unreachable functions: {len(data['unused_functions'])}")
424
+ if data["unused_imports"]:
425
+ print(f" * Unused imports: {len(data['unused_imports'])}")
426
+ if data["unused_classes"]:
427
+ print(f" * Unused classes: {len(data['unused_classes'])}")
428
+ if data["unused_variables"]:
429
+ print(f" * Unused variables: {len(data['unused_variables'])}")
430
+ if enable_danger:
431
+ print(f" * Security issues: {danger_count}")
432
+ if enable_secrets:
433
+ print(f" * Secrets found: {secrets_count}")
434
+
435
+ if data["unused_functions"]:
436
+ print("\n - Unreachable Functions")
437
+ print("=======================")
438
+ for i, func in enumerate(data["unused_functions"], 1):
439
+ print(f" {i}. {func['name']}")
440
+ print(f" └─ {func['file']}:{func['line']}")
441
+
442
+ if data["unused_imports"]:
443
+ print("\n - Unused Imports")
444
+ print("================")
445
+ for i, imp in enumerate(data["unused_imports"], 1):
446
+ print(f" {i}. {imp['simple_name']}")
447
+ print(f" └─ {imp['file']}:{imp['line']}")
448
+
449
+ if data["unused_classes"]:
450
+ print("\n - Unused Classes")
451
+ print("=================")
452
+ for i, cls in enumerate(data["unused_classes"], 1):
453
+ print(f" {i}. {cls['name']}")
454
+ print(f" └─ {cls['file']}:{cls['line']}")
455
+
456
+ if data["unused_variables"]:
457
+ print("\n - Unused Variables")
458
+ print("==================")
459
+ for i, var in enumerate(data["unused_variables"], 1):
460
+ print(f" {i}. {var['name']}")
461
+ print(f" └─ {var['file']}:{var['line']}")
462
+
463
+ if enable_danger and data.get("danger"):
464
+ print("\n - Security Issues")
465
+ print("================")
466
+ for i, f in enumerate(data["danger"], 1):
467
+ print(f" {i}. {f['message']} [{f['rule_id']}] ({f['file']}:{f['line']}) Severity: {f['severity']}")
468
+
469
+ if enable_secrets and data.get("secrets"):
470
+ print("\n - Secrets")
471
+ print("==========")
472
+ for i, s in enumerate(data["secrets"], 1):
473
+ rid = s.get("rule_id", "SECRET")
474
+ msg = s.get("message", "Potential secret")
475
+ file = s.get("file")
476
+ line = s.get("line", 1)
477
+ sev = s.get("severity", "HIGH")
478
+ print(f" {i}. {msg} [{rid}] ({file}:{line}) Severity: {sev}")
479
+
480
+ print("\n" + "─" * 50)
481
+ if enable_danger:
482
+ print(f"Found {total_dead} dead code items and {danger_count} security flaws. Add this badge to your README:")
438
483
  else:
439
- print("Usage: python Skylos.py <path> [confidence_threshold]")
484
+ print(f"Found {total_dead} dead code items. Add this badge to your README:")
485
+ print("```markdown")
486
+ print(f"![Dead Code: {total_dead}](https://img.shields.io/badge/Dead_Code-{total_dead}_detected-orange?logo=codacy&logoColor=red)")
487
+ print("```")
488
+
489
+ print("\nNext steps:")
490
+ print(" * Use --interactive to select specific items to remove")
491
+ print(" * Use --dry-run to preview changes before applying them")