Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix JSON output when using -j or --json option #54

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
28 changes: 28 additions & 0 deletions .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Test Suite

on:
pull_request:
branches:
- main
- develop

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Perl
run: |
sudo apt-get update
sudo apt-get install -y perl
sudo apt install -y perl cpanminus

- name: Install dependencies
run: sudo cpanm --installdeps --with-test .

- name: Run tests
working-directory: ./tests
run: prove -r
8 changes: 7 additions & 1 deletion cpanfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
requires "Mojo::UserAgent";
requires "YAML::Tiny", "1.73";
requires "Find::Lib", "1.04";
requires "JSON", "4.07";
requires "JSON", "4.07";
requires "IO::Interactive";

on 'test' => sub {
requires "Test::More";
requires "Test::MockModule";
};
42 changes: 23 additions & 19 deletions lib/Engine/Fuzzer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,52 @@ package Engine::Fuzzer {
sub new {
my ($self, $timeout, $headers, $skipssl, $proxy) = @_;

my $userAgent = Mojo::UserAgent -> new() -> request_timeout($timeout) -> insecure($skipssl);
my $userAgent = Mojo::UserAgent->new()->request_timeout($timeout)->insecure($skipssl);

if ($proxy) {
$userAgent -> proxy -> http($proxy);
$userAgent -> proxy -> https($proxy);
$userAgent->proxy->http($proxy);
$userAgent->proxy->https($proxy);
}

bless {
my $instance = bless {
useragent => $userAgent,
headers => $headers
}, $self;

return $instance;
}

sub request {
my ($self, $method, $agent, $endpoint, $payload, $accept) = @_;

my $request = $self -> {useragent} -> build_tx (
my $request = $self->{useragent}->build_tx(
$method => $endpoint => {
"User-Agent" => $agent,
%{$self -> {headers}}
%{$self->{headers}}
} => $payload || ""
);

try {
my $response = $self -> {useragent} -> start($request) -> result();
my $result;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just define the $result as 0 and remove the catch function below


my $result = {
try {
my $response = $self->{useragent}->start($request)->result();

$result = {
"Method" => $method,
"URL" => $endpoint,
"Code" => $response -> code(),
"Response" => $response -> message(),
"Content" => $response -> body(),
"Length" => $response -> headers() -> content_length() || "0"
"Code" => $response->code(),
"Response" => $response->message(),
"Content" => $response->body(),
"Length" => $response->headers()->content_length() || "0"
};

return $result;
}

catch {
return 0;
}
$result = 0;
};

return $result;
}
}

1;
1;
56 changes: 35 additions & 21 deletions lib/Engine/FuzzerThread.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package Engine::FuzzerThread {
use threads;
use warnings;
use Engine::Fuzzer;
use threads::shared;

my $is_first_json_element :shared = 1;

sub new {
my (
Expand All @@ -23,13 +26,12 @@ package Engine::FuzzerThread {

if ($length) {
($cmp, $length) = $length =~ /([>=<]{0,2})(\d+)/;

$cmp = sub { $_[0] >= $length } if ($cmp eq ">=");
$cmp = sub { $_[0] <= $length } if ($cmp eq "<=");
$cmp = sub { $_[0] != $length } if ($cmp eq "<>");
$cmp = sub { $_[0] > $length } if ($cmp eq ">");
$cmp = sub { $_[0] < $length } if ($cmp eq "<");
$cmp = sub { $_[0] == $length } if (!$cmp or $cmp eq "=");
$cmp = sub { $_[0] == $length } if (defined($cmp) || $cmp eq "=");
}

async {
Expand All @@ -40,30 +42,42 @@ package Engine::FuzzerThread {
for my $verb (@verbs) {
my $result = $fuzzer -> request($verb, $agent, $endpoint, $payload, $accept);

unless ($result) {
next;
}
next unless $result;

my $status = $result -> {Code};

if (grep(/^$status$/, @invalid_codes) || ($return && !grep(/^$status$/, @valid_codes))) {
next;
}

if ($length && !($cmp -> ($result -> {Length}))) {
next;
}

my $message = $json ? $format -> encode($result) : sprintf(
"Code: %d | URL: %s | Method: %s | Response: %s | Length: %s",
$status, $result -> {URL}, $result -> {Method},
$result -> {Response} || "?", $result -> {Length}
);
next if scalar(grep { $_ eq $status } @invalid_codes) ||
($return && !scalar(grep { $_ eq $status } @valid_codes));

print $message, "\n" if !$content || $result -> {Content} =~ m/$content/;
next if $length && !($cmp -> ($result -> {Length}));

sleep($delay);
$found = 1;
sleep($delay);

my $output_handler = sub {
my ($result) = @_;
lock($is_first_json_element);

my $json_str = $format -> encode($result);
my $output = $is_first_json_element ?
$json_str :
",\n$json_str";

$is_first_json_element = 0;
return $output;
};

my $plain_handler = sub {
my ($result) = @_;
return sprintf(
"Code: %d | URL: %s | Method: %s | Response: %s | Length: %s\n",
$status, $result -> {URL}, $result -> {Method},
$result -> {Response} || "?", $result -> {Length}
);
};

my $handler = $json ? $output_handler : $plain_handler;
print $handler->($result);
}
}
};
Expand All @@ -72,4 +86,4 @@ package Engine::FuzzerThread {
}
}

1;
1;
40 changes: 25 additions & 15 deletions lib/Engine/Orchestrator.pm
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package Engine::Orchestrator {
package Engine::Orchestrator {
use strict;
use threads;
use warnings;
use Fcntl qw(O_RDONLY);
use Engine::FuzzerThread;

my $wordlist_queue;
Expand All @@ -13,20 +14,22 @@ package Engine::Orchestrator {
for (1 .. $number) {
return unless (@{$list} > 0);

if (eof($list -> [0])) {
if (eof($list->[0])) {
close shift @{$list};

(@{$list} > 0) || $wordlist_queue -> end();
(@{$list} > 0) || $wordlist_queue->end();

next
next;
}

my $filehandle = $list -> [0];
my $filehandle = $list->[0];

chomp(my $line = <$filehandle>);

$wordlist_queue -> enqueue($line);
$wordlist_queue->enqueue($line);
}

return;
}

sub add_target {
Expand All @@ -39,6 +42,8 @@ package Engine::Orchestrator {

push @targets_queue, $target;
}

return;
}

sub run_fuzzer {
Expand All @@ -51,27 +56,30 @@ package Engine::Orchestrator {
$target = shift @targets_queue;
}

$self -> threaded_fuzz($target, %options);
$self->threaded_fuzz($target, %options);
}

return 0;
}

sub threaded_fuzz {
my ($self, $target, %options) = @_;

my @current = map {
open(my $filehandle, "<$_") || die "$0: Can't open $_: $!";
my $filehandle;
sysopen($filehandle, $_, O_RDONLY) || die "$0: Can't open $_: $!";

$filehandle
$filehandle;
} glob($options{wordlist});

$wordlist_queue = Thread::Queue -> new();
$wordlist_queue = Thread::Queue->new();

use constant CONCURRENT_TASKS => 10;
my $CONCURRENT_TASKS = 10;

fill_queue(\@current, CONCURRENT_TASKS * $options{tasks});
fill_queue(\@current, $CONCURRENT_TASKS * $options{tasks});

for (1 .. $options{tasks}) {
Engine::FuzzerThread -> new (
Engine::FuzzerThread->new(
$wordlist_queue,
$target,
$options{method},
Expand All @@ -92,11 +100,13 @@ package Engine::Orchestrator {
);
}

while (threads -> list(threads::running) > 0) {
while (threads->list(threads::running) > 0) {
fill_queue(\@current, $options{tasks});
}

map { $_ -> join() } threads -> list(threads::all);
map { $_->join() } threads->list(threads::all);

close $_ for @current;

return 0;
}
Expand Down
14 changes: 12 additions & 2 deletions nozaki.pl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use threads;
use warnings;
use Thread::Queue;
use IO::Interactive;
use Find::Lib "./lib";
use Functions::Helper;
use Functions::Parser;
Expand Down Expand Up @@ -48,6 +49,8 @@ sub main {

return Functions::Helper -> new() unless @targets;

print "[\n" if $options{json};

if ($workflow) {
my $rules = Functions::Parser -> new($workflow);

Expand All @@ -66,7 +69,14 @@ sub main {

Engine::Orchestrator::add_target(@targets);

return Engine::Orchestrator -> run_fuzzer(%options);
Engine::Orchestrator -> run_fuzzer(%options);

if ($options{json}) {
print "]\n";
truncate STDOUT, tell(STDOUT) - 2 if IO::Interactive::is_interactive();
}

return 0;
}

exit main() unless caller;
exit main() unless caller;
Loading