Skip to content

Commit 1141118

Browse files
committed
Add proxy rotation support to AnalyzeService and AttackService with corresponding tests
1 parent a5a37a0 commit 1141118

4 files changed

Lines changed: 536 additions & 4 deletions

File tree

src/ciberwebscan/services/analyze_service.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,53 @@ def __init__(self):
125125
self.app_config.user_agent
126126
)
127127

128+
# Initialize proxy rotator from config
129+
self._proxy_rotator = self._build_proxy_rotator()
130+
131+
def _build_proxy_rotator(self):
132+
"""Build a ProxyRotator from config if proxy rotation is enabled."""
133+
from ciberwebscan.core.client.proxy import ProxyRotator
134+
135+
proxy_cfg = self.app_config.http.proxy
136+
if proxy_cfg is None or not proxy_cfg.rotate:
137+
return None
138+
139+
proxies: list[str] = []
140+
if proxy_cfg.proxy_list:
141+
proxies = list(proxy_cfg.proxy_list)
142+
else:
143+
for url in (proxy_cfg.http, proxy_cfg.https):
144+
if url is not None:
145+
proxies.append(str(url))
146+
if proxy_cfg.socks5:
147+
proxies.append(proxy_cfg.socks5)
148+
149+
if not proxies:
150+
logger.warning(
151+
"Proxy rotation enabled but no proxies configured — "
152+
"set proxy_list or individual proxy fields"
153+
)
154+
return None
155+
156+
rotator = ProxyRotator(
157+
proxies=proxies,
158+
rotation_interval=proxy_cfg.rotation_interval,
159+
)
160+
logger.info(
161+
"Proxy rotation enabled: %d proxies, interval=%d",
162+
len(proxies),
163+
proxy_cfg.rotation_interval,
164+
)
165+
return rotator
166+
167+
def _resolve_proxy(self, explicit_proxy: str | None) -> str | None:
168+
"""Return *explicit_proxy* when provided, otherwise ask the rotator."""
169+
if explicit_proxy:
170+
return explicit_proxy
171+
if self._proxy_rotator:
172+
return self._proxy_rotator.next()
173+
return None
174+
128175
@property
129176
def ssl_analyzer(self) -> SSLAnalyzer:
130177
"""Get or create SSL analyzer instance."""
@@ -454,7 +501,7 @@ def _analyze_headers(
454501
with HTTPClient(
455502
timeout=timeout,
456503
default_headers=default_headers or None,
457-
proxy=options.proxy,
504+
proxy=self._resolve_proxy(options.proxy),
458505
) as client:
459506
resp = client.get(url, cookies=options.cookies or None)
460507

@@ -523,7 +570,7 @@ def _fingerprint(
523570
with HTTPClient(
524571
timeout=timeout,
525572
default_headers=default_headers or None,
526-
proxy=options.proxy,
573+
proxy=self._resolve_proxy(options.proxy),
527574
) as client:
528575
resp = client.get(url, cookies=options.cookies or None)
529576
headers = dict(resp.headers)

src/ciberwebscan/services/attack_service.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,53 @@ def __init__(self):
118118
self.app_config.user_agent
119119
)
120120

121+
# Initialize proxy rotator from config
122+
self._proxy_rotator = self._build_proxy_rotator()
123+
124+
def _build_proxy_rotator(self):
125+
"""Build a ProxyRotator from config if proxy rotation is enabled."""
126+
from ciberwebscan.core.client.proxy import ProxyRotator
127+
128+
proxy_cfg = self.app_config.http.proxy
129+
if proxy_cfg is None or not proxy_cfg.rotate:
130+
return None
131+
132+
proxies: list[str] = []
133+
if proxy_cfg.proxy_list:
134+
proxies = list(proxy_cfg.proxy_list)
135+
else:
136+
for url in (proxy_cfg.http, proxy_cfg.https):
137+
if url is not None:
138+
proxies.append(str(url))
139+
if proxy_cfg.socks5:
140+
proxies.append(proxy_cfg.socks5)
141+
142+
if not proxies:
143+
logger.warning(
144+
"Proxy rotation enabled but no proxies configured — "
145+
"set proxy_list or individual proxy fields"
146+
)
147+
return None
148+
149+
rotator = ProxyRotator(
150+
proxies=proxies,
151+
rotation_interval=proxy_cfg.rotation_interval,
152+
)
153+
logger.info(
154+
"Proxy rotation enabled: %d proxies, interval=%d",
155+
len(proxies),
156+
proxy_cfg.rotation_interval,
157+
)
158+
return rotator
159+
160+
def _resolve_proxy(self, explicit_proxy: str | None) -> str | None:
161+
"""Return *explicit_proxy* when provided, otherwise ask the rotator."""
162+
if explicit_proxy:
163+
return explicit_proxy
164+
if self._proxy_rotator:
165+
return self._proxy_rotator.next()
166+
return None
167+
121168
def attack(self, options: AttackOptions) -> ServiceResult[AttackResult]:
122169
"""
123170
Perform security attack simulations.
@@ -211,7 +258,7 @@ def attack(self, options: AttackOptions) -> ServiceResult[AttackResult]:
211258
verify=http_config.verify_ssl,
212259
follow_redirects=http_config.follow_redirects,
213260
default_headers=default_headers or None,
214-
proxy=options.proxy,
261+
proxy=self._resolve_proxy(options.proxy),
215262
)
216263

217264
# Create attack context

0 commit comments

Comments
 (0)