|
10 | 10 | JTextField, |
11 | 11 | ButtonGroup, |
12 | 12 | JRadioButton, |
| 13 | + JCheckBox, |
13 | 14 | ) |
14 | 15 | from java.awt import BorderLayout, FlowLayout, Dimension |
15 | 16 | from java.lang import Short |
@@ -112,6 +113,13 @@ def create_scanner_tab(self): |
112 | 113 | target_panel.add(type_panel) |
113 | 114 | target_panel.add(target_label) |
114 | 115 | target_panel.add(self._target_input) |
| 116 | + |
| 117 | + # Add redirect checkbox after target input |
| 118 | + self.follow_redirects = JCheckBox("Follow Redirects", True) |
| 119 | + self.follow_redirects.setBackground(self.DARK_BACKGROUND) |
| 120 | + self.follow_redirects.setForeground(self.DREADNODE_ORANGE) |
| 121 | + target_panel.add(self.follow_redirects) |
| 122 | + |
115 | 123 | target_panel.setMaximumSize(Dimension(Short.MAX_VALUE, 35)) |
116 | 124 |
|
117 | 125 | # System prompt panel |
@@ -368,38 +376,66 @@ def analyze_url(self, url): |
368 | 376 | if not re.match(r"^https?://", url): |
369 | 377 | url = "http://" + url |
370 | 378 |
|
371 | | - req = urllib2.Request(url) |
372 | | - response = urllib2.urlopen(req) |
373 | | - |
374 | | - # Gather information about the target |
375 | | - headers = dict(response.info().items()) |
376 | | - content = response.read() |
| 379 | + opener = urllib2.build_opener() |
| 380 | + if not self.follow_redirects.isSelected(): |
| 381 | + # Don't follow redirects if checkbox is unchecked |
| 382 | + opener.handler_order = dict( |
| 383 | + [(h, i) for i, h in enumerate(opener.handlers)] |
| 384 | + ) |
| 385 | + no_redirect_handler = urllib2.HTTPRedirectHandler() |
| 386 | + no_redirect_handler.max_redirections = 0 |
| 387 | + opener.handlers = [ |
| 388 | + h |
| 389 | + for h in opener.handlers |
| 390 | + if not isinstance(h, urllib2.HTTPRedirectHandler) |
| 391 | + ] |
377 | 392 |
|
378 | | - security_info = { |
379 | | - "url": url, |
380 | | - "status_code": response.getcode(), |
381 | | - "headers": headers, |
382 | | - "server_info": headers.get("server", "Unknown"), |
383 | | - "security_headers": { |
384 | | - "x-frame-options": headers.get("x-frame-options", "Not Set"), |
385 | | - "content-security-policy": headers.get( |
386 | | - "content-security-policy", "Not Set" |
387 | | - ), |
388 | | - "strict-transport-security": headers.get( |
389 | | - "strict-transport-security", "Not Set" |
390 | | - ), |
391 | | - "x-xss-protection": headers.get("x-xss-protection", "Not Set"), |
392 | | - "x-content-type-options": headers.get( |
393 | | - "x-content-type-options", "Not Set" |
394 | | - ), |
395 | | - }, |
396 | | - "response_size": len(content), |
397 | | - } |
398 | | - |
399 | | - return security_info |
| 393 | + req = urllib2.Request(url) |
| 394 | + try: |
| 395 | + response = opener.open(req) |
| 396 | + final_url = response.geturl() # Get final URL after redirects |
| 397 | + |
| 398 | + # Gather information about the target |
| 399 | + headers = dict(response.info().items()) |
| 400 | + content = response.read() |
| 401 | + |
| 402 | + security_info = { |
| 403 | + "initial_url": url, |
| 404 | + "final_url": final_url, |
| 405 | + "redirected": final_url != url, |
| 406 | + "status_code": response.getcode(), |
| 407 | + "headers": headers, |
| 408 | + "server_info": headers.get("server", "Unknown"), |
| 409 | + "security_headers": { |
| 410 | + "x-frame-options": headers.get("x-frame-options", "Not Set"), |
| 411 | + "content-security-policy": headers.get( |
| 412 | + "content-security-policy", "Not Set" |
| 413 | + ), |
| 414 | + "strict-transport-security": headers.get( |
| 415 | + "strict-transport-security", "Not Set" |
| 416 | + ), |
| 417 | + "x-xss-protection": headers.get("x-xss-protection", "Not Set"), |
| 418 | + "x-content-type-options": headers.get( |
| 419 | + "x-content-type-options", "Not Set" |
| 420 | + ), |
| 421 | + }, |
| 422 | + "response_size": len(content), |
| 423 | + } |
| 424 | + |
| 425 | + return security_info |
| 426 | + |
| 427 | + except urllib2.HTTPError as e: |
| 428 | + # Handle HTTP errors (like 301, 302 etc) when not following redirects |
| 429 | + security_info = { |
| 430 | + "initial_url": url, |
| 431 | + "error": "HTTP Error %d: %s" % (e.code, e.reason), |
| 432 | + "status_code": e.code, |
| 433 | + "headers": dict(e.headers.items()), |
| 434 | + } |
| 435 | + return security_info |
400 | 436 |
|
401 | 437 | except Exception as e: |
402 | | - self._scanner_output.setText("Error analyzing URL: {str(e)}") |
| 438 | + self._scanner_output.setText("Error analyzing URL: %s" % str(e)) |
403 | 439 | return None |
404 | 440 |
|
405 | 441 | def load_prompt_template(self, is_openapi=False): |
|
0 commit comments