Skip to content

Commit a6fe76d

Browse files
committed
Merge remote-tracking branch 'origin/fix/259-nuclei-scan' into test/release-2.2.0
2 parents e86d1db + e023685 commit a6fe76d

File tree

2 files changed

+108
-33
lines changed

2 files changed

+108
-33
lines changed

web/reNgine/common_func.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,10 +1315,12 @@ def execute_command(command, shell, cwd):
13151315
return subprocess.Popen(
13161316
command,
13171317
stdout=subprocess.PIPE,
1318-
stderr=subprocess.STDOUT,
1319-
universal_newlines=True,
1318+
stderr=subprocess.PIPE,
13201319
shell=shell,
1321-
cwd=cwd
1320+
cwd=cwd,
1321+
bufsize=-1,
1322+
universal_newlines=True,
1323+
encoding='utf-8'
13221324
)
13231325

13241326
def get_data_from_post_request(request, field):

web/reNgine/tasks.py

Lines changed: 103 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,23 +2553,33 @@ def nuclei_scan(self, urls=[], ctx={}, description=None):
25532553
return
25542554

25552555
if intensity == 'normal': # reduce number of endpoints to scan
2556+
if not os.path.exists(input_path):
2557+
with open(input_path, 'w') as f:
2558+
f.write('\n'.join(urls))
2559+
25562560
unfurl_filter = str(Path(self.results_dir) / 'urls_unfurled.txt')
2561+
25572562
run_command(
25582563
f"cat {input_path} | unfurl -u format %s://%d%p |uro > {unfurl_filter}",
25592564
shell=True,
25602565
history_file=self.history_file,
25612566
scan_id=self.scan_id,
25622567
activity_id=self.activity_id)
25632568
run_command(
2564-
f'sort -u {unfurl_filter} -o {unfurl_filter}',
2569+
f'sort -u {unfurl_filter} -o {unfurl_filter}',
25652570
shell=True,
25662571
history_file=self.history_file,
25672572
scan_id=self.scan_id,
25682573
activity_id=self.activity_id)
2574+
2575+
if not os.path.exists(unfurl_filter) or os.path.getsize(unfurl_filter) == 0:
2576+
logger.error(f"Failed to create or empty unfurled URLs file at {unfurl_filter}")
2577+
unfurl_filter = input_path
2578+
25692579
input_path = unfurl_filter
25702580

25712581
# Build templates
2572-
# logger.info('Updating Nuclei templates ...')
2582+
logger.info('Updating Nuclei templates ...')
25732583
run_command(
25742584
'nuclei -update-templates',
25752585
shell=True,
@@ -2588,8 +2598,11 @@ def nuclei_scan(self, urls=[], ctx={}, description=None):
25882598
templates.extend(nuclei_templates)
25892599

25902600
if custom_nuclei_templates:
2591-
custom_nuclei_template_paths = [f'{str(elem)}.yaml' for elem in custom_nuclei_templates]
2592-
template = templates.extend(custom_nuclei_template_paths)
2601+
custom_nuclei_template_paths = [
2602+
str(Path(NUCLEI_DEFAULT_TEMPLATES_PATH) / f'{str(elem)}.yaml')
2603+
for elem in custom_nuclei_templates
2604+
]
2605+
templates.extend(custom_nuclei_template_paths)
25932606

25942607
# Build CMD
25952608
cmd = 'nuclei -j'
@@ -2633,7 +2646,6 @@ def nuclei_scan(self, urls=[], ctx={}, description=None):
26332646
logger.info('Vulnerability scan with all severities completed...')
26342647

26352648
return None
2636-
26372649
@app.task(name='dalfox_xss_scan', queue='main_scan_queue', base=RengineTask, bind=True)
26382650
def dalfox_xss_scan(self, urls=[], ctx={}, description=None):
26392651
"""XSS Scan using dalfox
@@ -3843,12 +3855,51 @@ def record_exists(model, data, exclude_keys=[]):
38433855
Returns:
38443856
bool: True if the record exists, False otherwise.
38453857
"""
3858+
def clean_request(request_str):
3859+
if not request_str:
3860+
return request_str
3861+
request_lines = request_str.split('\r\n')
3862+
cleaned_lines = [line for line in request_lines if not line.startswith('User-Agent:')]
3863+
return '\r\n'.join(cleaned_lines)
38463864

38473865
# Extract the keys that will be used for the lookup
3848-
lookup_fields = {key: data[key] for key in data if key not in exclude_keys}
3866+
lookup_fields = data.copy()
3867+
3868+
# Clean the request field if it contains a User-Agent line
3869+
if 'request' in lookup_fields:
3870+
lookup_fields['request'] = clean_request(lookup_fields['request'])
38493871

3850-
# Return True if a record exists based on the lookup fields, False otherwise
3851-
return model.objects.filter(**lookup_fields).exists()
3872+
# Remove the fields to exclude
3873+
lookup_fields = {key: lookup_fields[key] for key in lookup_fields if key not in exclude_keys}
3874+
3875+
# Get all existing records that might match
3876+
base_query = {key: value for key, value in lookup_fields.items() if key != 'request'}
3877+
existing_records = model.objects.filter(**base_query)
3878+
3879+
if not existing_records.exists():
3880+
logger.debug(f"No existing records found with lookup fields: {lookup_fields}")
3881+
return False
3882+
3883+
# For each existing record, log the differences
3884+
for record in existing_records:
3885+
differences = {}
3886+
for key, value in lookup_fields.items():
3887+
existing_value = getattr(record, key)
3888+
if key == 'request':
3889+
existing_value = clean_request(existing_value)
3890+
if existing_value != value:
3891+
differences[key] = {
3892+
'existing': existing_value,
3893+
'new': value
3894+
}
3895+
3896+
if differences:
3897+
logger.debug(f"Record {record.id} has differences: {differences}")
3898+
else:
3899+
logger.debug(f"Record {record.id} matches exactly with lookup fields: {lookup_fields}")
3900+
return True
3901+
3902+
return False
38523903

38533904
@app.task(name='geo_localize', bind=False, queue='geo_localize_queue')
38543905
def geo_localize(host, ip_id=None):
@@ -4406,31 +4457,32 @@ def run_command(cmd, cwd=None, shell=False, history_file=None, scan_id=None, act
44064457
Returns:
44074458
tuple: A tuple containing the return code and output of the command.
44084459
"""
4409-
logger.info(f"Executing command: {cmd}")
4460+
logger.info(f"Starting execution of command: {cmd}")
44104461
command_obj = create_command_object(cmd, scan_id, activity_id)
44114462
command = prepare_command(cmd, shell)
44124463
logger.debug(f"Prepared run command: {command}")
4413-
4414-
process = execute_command(command, shell, cwd)
4415-
output = ''
4416-
for stdout_line in iter(process.stdout.readline, ""):
4417-
item = stdout_line.strip()
4418-
output += '\n' + item
4419-
logger.debug(item)
44204464

4421-
process.stdout.close()
4422-
process.wait()
4465+
process = execute_command(command, shell, cwd)
4466+
output, error_output = process.communicate()
44234467
return_code = process.returncode
4424-
command_obj.output = output
4468+
4469+
if output:
4470+
output = re.sub(r'\x1b\[[0-9;]*[mGKH]', '', output) if remove_ansi_sequence else output
4471+
4472+
if return_code != 0:
4473+
error_msg = f"Command failed with exit code {return_code}"
4474+
if error_output:
4475+
error_msg += f"\nError output:\n{error_output}"
4476+
logger.error(error_msg)
4477+
4478+
command_obj.output = output or None
4479+
command_obj.error_output = error_output or None
44254480
command_obj.return_code = return_code
44264481
command_obj.save()
4427-
4482+
44284483
if history_file:
44294484
write_history(history_file, cmd, return_code, output)
44304485

4431-
if remove_ansi_sequence:
4432-
output = remove_ansi_escape_sequences(output)
4433-
44344486
return return_code, output
44354487

44364488
def stream_command(cmd, cwd=None, shell=False, history_file=None, encoding='utf-8', scan_id=None, activity_id=None, trunc_char=None):
@@ -4457,20 +4509,41 @@ def stream_command(cmd, cwd=None, shell=False, history_file=None, encoding='utf-
44574509

44584510
process = execute_command(command, shell, cwd)
44594511
output = ""
4512+
error_output = ""
44604513

4461-
for line in iter(process.stdout.readline, b''):
4462-
if not line:
4514+
while True:
4515+
stdout_data = process.stdout.readline()
4516+
stderr_data = process.stderr.readline()
4517+
4518+
if not stdout_data and not stderr_data and process.poll() is not None:
44634519
break
4464-
item = process_line(line, trunc_char)
4465-
yield item
4466-
output += line
4467-
command_obj.output = output
4468-
command_obj.save()
4520+
4521+
if stdout_data:
4522+
output += stdout_data
4523+
try:
4524+
item = process_line(stdout_data, trunc_char)
4525+
if item:
4526+
yield item
4527+
except Exception as e:
4528+
logger.error(f"Error processing output line: {e}")
4529+
4530+
if stderr_data:
4531+
error_output += stderr_data
44694532

44704533
process.wait()
44714534
return_code = process.returncode
4535+
4536+
if return_code != 0:
4537+
error_msg = f"Command failed with exit code {return_code}"
4538+
if error_output:
4539+
error_msg += f"\nError output:\n{error_output}"
4540+
logger.error(error_msg)
4541+
4542+
command_obj.output = output or None
4543+
command_obj.error_output = error_output or None
44724544
command_obj.return_code = return_code
44734545
command_obj.save()
4546+
44744547
logger.debug(f'Command returned exit code: {return_code}')
44754548

44764549
if history_file:

0 commit comments

Comments
 (0)