secator 0.5.0__tar.gz → 0.5.2__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 secator might be problematic. Click here for more details.

Files changed (167) hide show
  1. {secator-0.5.0 → secator-0.5.2}/CHANGELOG.md +15 -0
  2. {secator-0.5.0 → secator-0.5.2}/PKG-INFO +1 -1
  3. {secator-0.5.0 → secator-0.5.2}/pyproject.toml +1 -1
  4. {secator-0.5.0 → secator-0.5.2}/secator/celery.py +4 -52
  5. {secator-0.5.0 → secator-0.5.2}/secator/config.py +1 -0
  6. {secator-0.5.0 → secator-0.5.2}/secator/output_types/url.py +3 -0
  7. {secator-0.5.0 → secator-0.5.2}/secator/report.py +4 -1
  8. {secator-0.5.0 → secator-0.5.2}/secator/tasks/httpx.py +17 -13
  9. {secator-0.5.0 → secator-0.5.2}/secator/tasks/katana.py +13 -7
  10. {secator-0.5.0 → secator-0.5.2}/secator/utils.py +3 -1
  11. {secator-0.5.0 → secator-0.5.2}/.flake8 +0 -0
  12. {secator-0.5.0 → secator-0.5.2}/.gitignore +0 -0
  13. {secator-0.5.0 → secator-0.5.2}/CONTRIBUTING.md +0 -0
  14. {secator-0.5.0 → secator-0.5.2}/Dockerfile +0 -0
  15. {secator-0.5.0 → secator-0.5.2}/LICENSE +0 -0
  16. {secator-0.5.0 → secator-0.5.2}/README.md +0 -0
  17. {secator-0.5.0 → secator-0.5.2}/SECURITY.md +0 -0
  18. {secator-0.5.0 → secator-0.5.2}/cloudbuild.yaml +0 -0
  19. {secator-0.5.0 → secator-0.5.2}/images/aliases.cast +0 -0
  20. {secator-0.5.0 → secator-0.5.2}/images/aliases.gif +0 -0
  21. {secator-0.5.0 → secator-0.5.2}/images/demo.gif +0 -0
  22. {secator-0.5.0 → secator-0.5.2}/images/demo.tap +0 -0
  23. {secator-0.5.0 → secator-0.5.2}/images/fmt.cast +0 -0
  24. {secator-0.5.0 → secator-0.5.2}/images/fmt.gif +0 -0
  25. {secator-0.5.0 → secator-0.5.2}/images/help.png +0 -0
  26. {secator-0.5.0 → secator-0.5.2}/images/input.cast +0 -0
  27. {secator-0.5.0 → secator-0.5.2}/images/input.gif +0 -0
  28. {secator-0.5.0 → secator-0.5.2}/images/pipe.cast +0 -0
  29. {secator-0.5.0 → secator-0.5.2}/images/pipe.gif +0 -0
  30. {secator-0.5.0 → secator-0.5.2}/images/short_demo.cast +0 -0
  31. {secator-0.5.0 → secator-0.5.2}/images/short_demo.gif +0 -0
  32. {secator-0.5.0 → secator-0.5.2}/scripts/download_cves.sh +0 -0
  33. {secator-0.5.0 → secator-0.5.2}/scripts/install.sh +0 -0
  34. {secator-0.5.0 → secator-0.5.2}/scripts/install_asciinema.sh +0 -0
  35. {secator-0.5.0 → secator-0.5.2}/scripts/install_go.sh +0 -0
  36. {secator-0.5.0 → secator-0.5.2}/scripts/install_ruby.sh +0 -0
  37. {secator-0.5.0 → secator-0.5.2}/scripts/msf/exploit_cve.rc +0 -0
  38. {secator-0.5.0 → secator-0.5.2}/scripts/msf/ftp_anonymous.rc +0 -0
  39. {secator-0.5.0 → secator-0.5.2}/scripts/msf/ftp_version.rc +0 -0
  40. {secator-0.5.0 → secator-0.5.2}/scripts/msf/ftp_vsftpd_234_backdoor.rc +0 -0
  41. {secator-0.5.0 → secator-0.5.2}/scripts/msf/redis.rc +0 -0
  42. {secator-0.5.0 → secator-0.5.2}/scripts/msfinstall.sh +0 -0
  43. {secator-0.5.0 → secator-0.5.2}/scripts/stories/STORY.md +0 -0
  44. {secator-0.5.0 → secator-0.5.2}/scripts/stories/aliases.sh +0 -0
  45. {secator-0.5.0 → secator-0.5.2}/scripts/stories/demo.sh +0 -0
  46. {secator-0.5.0 → secator-0.5.2}/scripts/stories/fmt.sh +0 -0
  47. {secator-0.5.0 → secator-0.5.2}/scripts/stories/input.sh +0 -0
  48. {secator-0.5.0 → secator-0.5.2}/scripts/stories/pipe.sh +0 -0
  49. {secator-0.5.0 → secator-0.5.2}/scripts/stories/short_demo.sh +0 -0
  50. {secator-0.5.0 → secator-0.5.2}/secator/.gitignore +0 -0
  51. {secator-0.5.0 → secator-0.5.2}/secator/__init__.py +0 -0
  52. {secator-0.5.0 → secator-0.5.2}/secator/cli.py +0 -0
  53. {secator-0.5.0 → secator-0.5.2}/secator/configs/__init__.py +0 -0
  54. {secator-0.5.0 → secator-0.5.2}/secator/configs/profiles/__init__.py +0 -0
  55. {secator-0.5.0 → secator-0.5.2}/secator/configs/profiles/aggressive.yaml +0 -0
  56. {secator-0.5.0 → secator-0.5.2}/secator/configs/profiles/default.yaml +0 -0
  57. {secator-0.5.0 → secator-0.5.2}/secator/configs/profiles/stealth.yaml +0 -0
  58. {secator-0.5.0 → secator-0.5.2}/secator/configs/scans/__init__.py +0 -0
  59. {secator-0.5.0 → secator-0.5.2}/secator/configs/scans/domain.yaml +0 -0
  60. {secator-0.5.0 → secator-0.5.2}/secator/configs/scans/host.yaml +0 -0
  61. {secator-0.5.0 → secator-0.5.2}/secator/configs/scans/network.yaml +0 -0
  62. {secator-0.5.0 → secator-0.5.2}/secator/configs/scans/subdomain.yaml +0 -0
  63. {secator-0.5.0 → secator-0.5.2}/secator/configs/scans/url.yaml +0 -0
  64. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/__init__.py +0 -0
  65. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/cidr_recon.yaml +0 -0
  66. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/code_scan.yaml +0 -0
  67. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/host_recon.yaml +0 -0
  68. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/port_scan.yaml +0 -0
  69. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/subdomain_recon.yaml +0 -0
  70. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/url_crawl.yaml +0 -0
  71. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/url_dirsearch.yaml +0 -0
  72. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/url_fuzz.yaml +0 -0
  73. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/url_nuclei.yaml +0 -0
  74. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/url_vuln.yaml +0 -0
  75. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/user_hunt.yaml +0 -0
  76. {secator-0.5.0 → secator-0.5.2}/secator/configs/workflows/wordpress.yaml +0 -0
  77. {secator-0.5.0 → secator-0.5.2}/secator/decorators.py +0 -0
  78. {secator-0.5.0 → secator-0.5.2}/secator/definitions.py +0 -0
  79. {secator-0.5.0 → secator-0.5.2}/secator/exporters/__init__.py +0 -0
  80. {secator-0.5.0 → secator-0.5.2}/secator/exporters/_base.py +0 -0
  81. {secator-0.5.0 → secator-0.5.2}/secator/exporters/csv.py +0 -0
  82. {secator-0.5.0 → secator-0.5.2}/secator/exporters/gdrive.py +0 -0
  83. {secator-0.5.0 → secator-0.5.2}/secator/exporters/json.py +0 -0
  84. {secator-0.5.0 → secator-0.5.2}/secator/exporters/table.py +0 -0
  85. {secator-0.5.0 → secator-0.5.2}/secator/exporters/txt.py +0 -0
  86. {secator-0.5.0 → secator-0.5.2}/secator/hooks/__init__.py +0 -0
  87. {secator-0.5.0 → secator-0.5.2}/secator/hooks/mongodb.py +0 -0
  88. {secator-0.5.0 → secator-0.5.2}/secator/installer.py +0 -0
  89. {secator-0.5.0 → secator-0.5.2}/secator/output_types/__init__.py +0 -0
  90. {secator-0.5.0 → secator-0.5.2}/secator/output_types/_base.py +0 -0
  91. {secator-0.5.0 → secator-0.5.2}/secator/output_types/exploit.py +0 -0
  92. {secator-0.5.0 → secator-0.5.2}/secator/output_types/ip.py +0 -0
  93. {secator-0.5.0 → secator-0.5.2}/secator/output_types/port.py +0 -0
  94. {secator-0.5.0 → secator-0.5.2}/secator/output_types/progress.py +0 -0
  95. {secator-0.5.0 → secator-0.5.2}/secator/output_types/record.py +0 -0
  96. {secator-0.5.0 → secator-0.5.2}/secator/output_types/subdomain.py +0 -0
  97. {secator-0.5.0 → secator-0.5.2}/secator/output_types/tag.py +0 -0
  98. {secator-0.5.0 → secator-0.5.2}/secator/output_types/target.py +0 -0
  99. {secator-0.5.0 → secator-0.5.2}/secator/output_types/user_account.py +0 -0
  100. {secator-0.5.0 → secator-0.5.2}/secator/output_types/vulnerability.py +0 -0
  101. {secator-0.5.0 → secator-0.5.2}/secator/rich.py +0 -0
  102. {secator-0.5.0 → secator-0.5.2}/secator/runners/__init__.py +0 -0
  103. {secator-0.5.0 → secator-0.5.2}/secator/runners/_base.py +0 -0
  104. {secator-0.5.0 → secator-0.5.2}/secator/runners/_helpers.py +0 -0
  105. {secator-0.5.0 → secator-0.5.2}/secator/runners/command.py +0 -0
  106. {secator-0.5.0 → secator-0.5.2}/secator/runners/scan.py +0 -0
  107. {secator-0.5.0 → secator-0.5.2}/secator/runners/task.py +0 -0
  108. {secator-0.5.0 → secator-0.5.2}/secator/runners/workflow.py +0 -0
  109. {secator-0.5.0 → secator-0.5.2}/secator/serializers/__init__.py +0 -0
  110. {secator-0.5.0 → secator-0.5.2}/secator/serializers/dataclass.py +0 -0
  111. {secator-0.5.0 → secator-0.5.2}/secator/serializers/json.py +0 -0
  112. {secator-0.5.0 → secator-0.5.2}/secator/serializers/regex.py +0 -0
  113. {secator-0.5.0 → secator-0.5.2}/secator/tasks/__init__.py +0 -0
  114. {secator-0.5.0 → secator-0.5.2}/secator/tasks/_categories.py +0 -0
  115. {secator-0.5.0 → secator-0.5.2}/secator/tasks/cariddi.py +0 -0
  116. {secator-0.5.0 → secator-0.5.2}/secator/tasks/dalfox.py +0 -0
  117. {secator-0.5.0 → secator-0.5.2}/secator/tasks/dirsearch.py +0 -0
  118. {secator-0.5.0 → secator-0.5.2}/secator/tasks/dnsx.py +0 -0
  119. {secator-0.5.0 → secator-0.5.2}/secator/tasks/dnsxbrute.py +0 -0
  120. {secator-0.5.0 → secator-0.5.2}/secator/tasks/feroxbuster.py +0 -0
  121. {secator-0.5.0 → secator-0.5.2}/secator/tasks/ffuf.py +0 -0
  122. {secator-0.5.0 → secator-0.5.2}/secator/tasks/fping.py +0 -0
  123. {secator-0.5.0 → secator-0.5.2}/secator/tasks/gau.py +0 -0
  124. {secator-0.5.0 → secator-0.5.2}/secator/tasks/gf.py +0 -0
  125. {secator-0.5.0 → secator-0.5.2}/secator/tasks/gospider.py +0 -0
  126. {secator-0.5.0 → secator-0.5.2}/secator/tasks/grype.py +0 -0
  127. {secator-0.5.0 → secator-0.5.2}/secator/tasks/h8mail.py +0 -0
  128. {secator-0.5.0 → secator-0.5.2}/secator/tasks/maigret.py +0 -0
  129. {secator-0.5.0 → secator-0.5.2}/secator/tasks/mapcidr.py +0 -0
  130. {secator-0.5.0 → secator-0.5.2}/secator/tasks/msfconsole.py +0 -0
  131. {secator-0.5.0 → secator-0.5.2}/secator/tasks/naabu.py +0 -0
  132. {secator-0.5.0 → secator-0.5.2}/secator/tasks/nmap.py +0 -0
  133. {secator-0.5.0 → secator-0.5.2}/secator/tasks/nuclei.py +0 -0
  134. {secator-0.5.0 → secator-0.5.2}/secator/tasks/searchsploit.py +0 -0
  135. {secator-0.5.0 → secator-0.5.2}/secator/tasks/subfinder.py +0 -0
  136. {secator-0.5.0 → secator-0.5.2}/secator/tasks/wpscan.py +0 -0
  137. {secator-0.5.0 → secator-0.5.2}/secator/template.py +0 -0
  138. {secator-0.5.0 → secator-0.5.2}/secator/utils_test.py +0 -0
  139. {secator-0.5.0 → secator-0.5.2}/tests/__init__.py +0 -0
  140. {secator-0.5.0 → secator-0.5.2}/tests/fixtures/h8mail_breach.txt +0 -0
  141. {secator-0.5.0 → secator-0.5.2}/tests/fixtures/ls.py +0 -0
  142. {secator-0.5.0 → secator-0.5.2}/tests/fixtures/msfconsole_input.rc +0 -0
  143. {secator-0.5.0 → secator-0.5.2}/tests/fixtures/nmap_output.xml +0 -0
  144. {secator-0.5.0 → secator-0.5.2}/tests/integration/__init__.py +0 -0
  145. {secator-0.5.0 → secator-0.5.2}/tests/integration/inputs.py +0 -0
  146. {secator-0.5.0 → secator-0.5.2}/tests/integration/outputs.py +0 -0
  147. {secator-0.5.0 → secator-0.5.2}/tests/integration/setup.sh +0 -0
  148. {secator-0.5.0 → secator-0.5.2}/tests/integration/teardown.sh +0 -0
  149. {secator-0.5.0 → secator-0.5.2}/tests/integration/test_scans.py +0 -0
  150. {secator-0.5.0 → secator-0.5.2}/tests/integration/test_tasks.py +0 -0
  151. {secator-0.5.0 → secator-0.5.2}/tests/integration/test_worker.py +0 -0
  152. {secator-0.5.0 → secator-0.5.2}/tests/integration/test_workflows.py +0 -0
  153. {secator-0.5.0 → secator-0.5.2}/tests/integration/wordlist.txt +0 -0
  154. {secator-0.5.0 → secator-0.5.2}/tests/integration/wordlist_dns.txt +0 -0
  155. {secator-0.5.0 → secator-0.5.2}/tests/integration/wordpress_toolbox/Dockerfile +0 -0
  156. {secator-0.5.0 → secator-0.5.2}/tests/integration/wordpress_toolbox/Makefile +0 -0
  157. {secator-0.5.0 → secator-0.5.2}/tests/performance/__init__.py +0 -0
  158. {secator-0.5.0 → secator-0.5.2}/tests/performance/loadtester.py +0 -0
  159. {secator-0.5.0 → secator-0.5.2}/tests/unit/__init__.py +0 -0
  160. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_celery.py +0 -0
  161. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_config.py +0 -0
  162. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_offline.py +0 -0
  163. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_scans.py +0 -0
  164. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_serializers.py +0 -0
  165. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_tasks.py +0 -0
  166. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_template.py +0 -0
  167. {secator-0.5.0 → secator-0.5.2}/tests/unit/test_workflows.py +0 -0
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.2](https://github.com/freelabz/secator/compare/v0.5.1...v0.5.2) (2024-05-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **nuclei,katana:** add -sr flag and write http responses and screenshot to correct folder ([#395](https://github.com/freelabz/secator/issues/395)) ([1a51790](https://github.com/freelabz/secator/commit/1a51790c9231f593631c2780b6d5e0fa89f1aa55))
9
+
10
+ ## [0.5.1](https://github.com/freelabz/secator/compare/v0.5.0...v0.5.1) (2024-05-06)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **output:** add headers to Url and print HTTP method when not GET ([#390](https://github.com/freelabz/secator/issues/390)) ([5a87d7b](https://github.com/freelabz/secator/commit/5a87d7b8bc1dd098999f3864952e98068fd32efc))
16
+ * **report:** do not remove duplicate in reports by default ([#392](https://github.com/freelabz/secator/issues/392)) ([7d74ae8](https://github.com/freelabz/secator/commit/7d74ae80bfd99c31714a5e7e25f2bd1caa642eb4))
17
+
3
18
  ## [0.5.0](https://github.com/freelabz/secator/compare/v0.4.1...v0.5.0) (2024-05-03)
4
19
 
5
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: secator
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: The pentester's swiss knife.
5
5
  Project-URL: Homepage, https://github.com/freelabz/secator
6
6
  Project-URL: Issues, https://github.com/freelabz/secator/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "secator"
7
- version = "0.5.0"
7
+ version = "0.5.2"
8
8
  authors = [{ name = "FreeLabz", email = "sales@freelabz.com" }]
9
9
  readme = "README.md"
10
10
  description = "The pentester's swiss knife."
@@ -1,11 +1,10 @@
1
1
  import gc
2
2
  import logging
3
3
  import traceback
4
- from time import sleep
5
4
 
6
5
  from celery import Celery, chain, chord, signals
7
6
  from celery.app import trace
8
- from celery.result import AsyncResult, allow_join_result
7
+ from celery.result import allow_join_result
9
8
  # from pyinstrument import Profiler # TODO: make pyinstrument optional
10
9
  from rich.logging import RichHandler
11
10
 
@@ -354,56 +353,9 @@ def forward_results(results):
354
353
  results = deduplicate(results, attr='_uuid')
355
354
  return results
356
355
 
357
-
358
- #---------------------#
359
- # Celery result utils #
360
- #---------------------#
361
-
362
-
363
- def poll_task(result, seen=[]):
364
- """Poll Celery result tree recursively to get results live.
365
-
366
- TODO: function is incomplete, as it does not parse all results.
367
-
368
- Args:
369
- result (Union[AsyncResult, GroupResult]): Celery result object.
370
- seen (list): List of seen results (do not yield again).
371
-
372
- Yields:
373
- dict: Result.
374
- """
375
- if result is None:
376
- return
377
-
378
- if result.children:
379
- for child in result.children:
380
- yield from poll_task(child, seen=seen)
381
- else:
382
- res = AsyncResult(result.id)
383
- if not res.info:
384
- sleep(0.1)
385
- yield from poll_task(result, seen=seen)
386
-
387
- # Task done running
388
- if isinstance(res.info, list):
389
- for item in res.info:
390
- if item._uuid not in seen:
391
- yield res.id, None, item
392
- seen.append(item._uuid)
393
- return
394
-
395
- # Get task partial results, remove duplicates
396
- results = res.info['results']
397
- name = res.info['name']
398
- for item in results:
399
- if item._uuid not in seen:
400
- yield res.id, name, item
401
- seen.append(item._uuid)
402
-
403
- # Task still running, keep polling
404
- if not res.ready():
405
- sleep(0.1)
406
- yield from poll_task(result, seen=seen)
356
+ #--------------#
357
+ # Celery utils #
358
+ #--------------#
407
359
 
408
360
 
409
361
  def is_celery_worker_alive():
@@ -74,6 +74,7 @@ class Runners(StrictModel):
74
74
  progress_update_frequency: int = 60
75
75
  skip_cve_search: bool = False
76
76
  skip_cve_low_confidence: bool = True
77
+ remove_duplicates: bool = False
77
78
 
78
79
 
79
80
  class HTTP(StrictModel):
@@ -23,6 +23,7 @@ class Url(OutputType):
23
23
  lines: int = field(default=0, compare=False)
24
24
  screenshot_path: str = field(default='', compare=False)
25
25
  stored_response_path: str = field(default='', compare=False)
26
+ headers: dict = field(default_factory=dict, repr=True, compare=False)
26
27
  _source: str = field(default='', repr=True, compare=False)
27
28
  _type: str = field(default='url', repr=True)
28
29
  _timestamp: int = field(default_factory=lambda: time.time(), compare=False)
@@ -55,6 +56,8 @@ class Url(OutputType):
55
56
 
56
57
  def __repr__(self):
57
58
  s = f'🔗 [white]{self.url}'
59
+ if self.method and self.method != 'GET':
60
+ s += f' \[[turquoise4]{self.method}[/]]'
58
61
  if self.status_code and self.status_code != 0:
59
62
  if self.status_code < 400:
60
63
  s += f' \[[green]{self.status_code}[/]]'
@@ -1,5 +1,6 @@
1
1
  import operator
2
2
 
3
+ from secator.config import CONFIG
3
4
  from secator.output_types import OUTPUT_TYPES, OutputType
4
5
  from secator.utils import merge_opts, get_file_timestamp
5
6
  from secator.rich import console
@@ -64,8 +65,10 @@ class Report:
64
65
  sort_by, _ = get_table_fields(output_type)
65
66
  items = [
66
67
  item for item in self.runner.results
67
- if isinstance(item, OutputType) and item._type == output_name and not item._duplicate
68
+ if isinstance(item, OutputType) and item._type == output_name
68
69
  ]
70
+ if CONFIG.runners.remove_duplicates:
71
+ items = [item for item in items if not item._duplicate]
69
72
  if items:
70
73
  if sort_by and all(sort_by):
71
74
  items = sorted(items, key=operator.attrgetter(*sort_by))
@@ -31,6 +31,7 @@ class httpx(Http):
31
31
  'cdn': {'is_flag': True, 'default': False, 'help': 'CDN detection'},
32
32
  'debug_resp': {'is_flag': True, 'default': False, 'help': 'Debug response'},
33
33
  'vhost': {'is_flag': True, 'default': False, 'help': 'Probe and display server supporting VHOST'},
34
+ 'store_responses': {'is_flag': True, 'short': 'sr', 'default': CONFIG.http.store_responses, 'help': 'Save HTTP responses'}, # noqa: E501
34
35
  'screenshot': {'is_flag': True, 'short': 'ss', 'default': False, 'help': 'Screenshot response'},
35
36
  'system_chrome': {'is_flag': True, 'default': False, 'help': 'Use local installed Chrome for screenshot'},
36
37
  'headless_options': {'is_flag': False, 'short': 'ho', 'default': None, 'help': 'Headless Chrome additional options'},
@@ -55,6 +56,7 @@ class httpx(Http):
55
56
  THREADS: 'threads',
56
57
  TIMEOUT: 'timeout',
57
58
  USER_AGENT: OPT_NOT_SUPPORTED,
59
+ 'store_responses': 'sr',
58
60
  }
59
61
  opt_value_map = {
60
62
  DELAY: lambda x: str(x) + 's' if x else None,
@@ -71,15 +73,10 @@ class httpx(Http):
71
73
  debug_resp = self.get_opt_value('debug_resp')
72
74
  if debug_resp:
73
75
  self.cmd = self.cmd.replace('-silent', '')
74
- if CONFIG.http.store_responses:
75
- self.output_response_path = f'{self.reports_folder}/response'
76
- self.output_screenshot_path = f'{self.reports_folder}/screenshot'
77
- os.makedirs(self.output_response_path, exist_ok=True)
78
- os.makedirs(self.output_screenshot_path, exist_ok=True)
79
- self.cmd += f' -sr -srd {self.reports_folder}'
80
-
81
- # Remove screenshot bytes and body bytes when screenshot
82
76
  screenshot = self.get_opt_value('screenshot')
77
+ store_responses = self.get_opt_value('store_responses')
78
+ if store_responses or screenshot:
79
+ self.cmd += f' -srd {self.reports_folder}/.outputs'
83
80
  if screenshot:
84
81
  self.cmd += ' -esb -ehb'
85
82
 
@@ -98,8 +95,15 @@ class httpx(Http):
98
95
 
99
96
  @staticmethod
100
97
  def on_end(self):
101
- if CONFIG.http.store_responses:
102
- if os.path.exists(self.output_response_path + '/index.txt'):
103
- os.remove(self.output_response_path + '/index.txt')
104
- if os.path.exists(self.output_screenshot_path + '/index.txt'):
105
- os.remove(self.output_screenshot_path + '/index_screenshot.txt')
98
+ store_responses = self.get_opt_value('store_responses')
99
+ response_dir = f'{self.reports_folder}/.outputs'
100
+ if store_responses:
101
+ index_rpath = f'{response_dir}/response/index.txt'
102
+ index_spath = f'{response_dir}/screenshot/index_screenshot.txt'
103
+ index_spath2 = f'{response_dir}/screenshot/screenshot.html'
104
+ if os.path.exists(index_rpath):
105
+ os.remove(index_rpath)
106
+ if os.path.exists(index_spath):
107
+ os.remove(index_spath)
108
+ if os.path.exists(index_spath2):
109
+ os.remove(index_spath2)
@@ -29,7 +29,8 @@ class katana(HttpCrawler):
29
29
  opts = {
30
30
  'headless': {'is_flag': True, 'short': 'hl', 'help': 'Headless mode'},
31
31
  'system_chrome': {'is_flag': True, 'short': 'sc', 'help': 'Use local installed chrome browser'},
32
- 'form_extraction': {'is_flag': True, 'short': 'fx', 'help': 'Detect forms'}
32
+ 'form_extraction': {'is_flag': True, 'short': 'fx', 'help': 'Detect forms'},
33
+ 'store_responses': {'is_flag': True, 'short': 'sr', 'default': CONFIG.http.store_responses, 'help': 'Store responses'}
33
34
  }
34
35
  opt_key_map = {
35
36
  HEADER: 'headers',
@@ -50,7 +51,8 @@ class katana(HttpCrawler):
50
51
  RETRIES: 'retry',
51
52
  THREADS: 'concurrency',
52
53
  TIMEOUT: 'timeout',
53
- USER_AGENT: OPT_NOT_SUPPORTED
54
+ USER_AGENT: OPT_NOT_SUPPORTED,
55
+ 'store_responses': 'sr'
54
56
  }
55
57
  opt_value_map = {
56
58
  DELAY: lambda x: int(x) if isinstance(x, float) else x
@@ -107,14 +109,16 @@ class katana(HttpCrawler):
107
109
  debug_resp = self.get_opt_value('debug_resp')
108
110
  if debug_resp:
109
111
  self.cmd = self.cmd.replace('-silent', '')
110
- if CONFIG.http.store_responses:
111
- self.cmd += f' -sr -srd {self.reports_folder}'
112
+ store_responses = self.get_opt_value('store_responses')
113
+ if store_responses:
114
+ self.cmd += f' -srd {self.reports_folder}/.outputs'
112
115
 
113
116
  @staticmethod
114
117
  def on_item(self, item):
115
118
  if not isinstance(item, Url):
116
119
  return item
117
- if CONFIG.http.store_responses and os.path.exists(item.stored_response_path):
120
+ store_responses = self.get_opt_value('store_responses')
121
+ if store_responses and os.path.exists(item.stored_response_path):
118
122
  with open(item.stored_response_path, 'r', encoding='latin-1') as fin:
119
123
  data = fin.read().splitlines(True)
120
124
  first_line = data[0]
@@ -126,5 +130,7 @@ class katana(HttpCrawler):
126
130
 
127
131
  @staticmethod
128
132
  def on_end(self):
129
- if CONFIG.http.store_responses and os.path.exists(self.reports_folder + '/index.txt'):
130
- os.remove(self.reports_folder + '/index.txt')
133
+ store_responses = self.get_opt_value('store_responses')
134
+ index_rpath = f'{self.reports_folder}/.outputs/index.txt'
135
+ if store_responses and os.path.exists(index_rpath):
136
+ os.remove(index_rpath)
@@ -343,8 +343,10 @@ def print_results_table(results, title=None, exclude_fields=[], log=False):
343
343
  if output_type.__name__ == 'Progress':
344
344
  continue
345
345
  items = [
346
- item for item in results if item._type == output_type.get_name() and not item._duplicate
346
+ item for item in results if item._type == output_type.get_name()
347
347
  ]
348
+ if CONFIG.runners.remove_duplicates:
349
+ items = [item for item in items if not item._duplicate]
348
350
  if items:
349
351
  _table = build_table(
350
352
  items,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes