require 'spec_helper'
require 'feroxbuster/parsers/json'

describe Feroxbuster::Parsers::JSON do
  describe ".parse" do
    let(:line) { json }
    let(:io)   { StringIO.new(line + $/) }

    let(:parsed_json) { JSON.parse(json) }

    context "when the JSON \"type\" attribute is \"response\"" do
      let(:json) do
        "{\"type\":\"response\",\"url\":\"https://github.com/test\",\"original_url\":\"https://github.com\",\"path\":\"/test\",\"wildcard\":false,\"status\":200,\"method\":\"GET\",\"content_length\":0,\"line_count\":2010,\"word_count\":11253,\"headers\":{\"permissions-policy\":\"interest-cohort=()\",\"accept-ranges\":\"bytes\",\"referrer-policy\":\"origin-when-cross-origin, strict-origin-when-cross-origin\",\"x-content-type-options\":\"nosniff\",\"cache-control\":\"max-age=0, private, must-revalidate\",\"expect-ct\":\"max-age=2592000, report-uri=\\\"https://api.github.com/_private/browser/errors\\\"\",\"server\":\"GitHub.com\",\"x-github-request-id\":\"85A8:0E10:20B03E:27719A:6260B1D5\",\"transfer-encoding\":\"chunked\",\"date\":\"Thu, 21 Apr 2022 01:22:16 GMT\",\"strict-transport-security\":\"max-age=31536000; includeSubdomains; preload\",\"etag\":\"W/\\\"7c98cb0440eb94eddcfd360497fae419\\\"\",\"x-frame-options\":\"deny\",\"content-security-policy\":\"default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com objects-origin.githubusercontent.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events translator.github.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com online.visualstudio.com/api/v1/locations github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src render.githubusercontent.com viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com github-cloud.s3.amazonaws.com secured-user-images.githubusercontent.com/ *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; worker-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/\",\"x-xss-protection\":\"0\",\"set-cookie\":\"logged_in=no; Path=/; Domain=github.com; Expires=Fri, 21 Apr 2023 01:22:29 GMT; HttpOnly; Secure; SameSite=Lax\",\"vary\":\"X-Requested-With, X-PJAX-Container, Accept-Encoding, Accept, X-Requested-With\",\"content-type\":\"text/html; charset=utf-8\"},\"extension\":\"\"}"
      end

      it "must parse each line and yield Feroxbuster::Response objects" do
        yielded_objects = []

        subject.parse(io) do |response|
          yielded_objects << response
        end

        expect(yielded_objects.length).to eq(1)
        expect(yielded_objects.first).to be_kind_of(Feroxbuster::Response)

        yielded_response = yielded_objects.first
        expect(yielded_response.url).to eq(parsed_json['url'])
        expect(yielded_response.original_url).to eq(parsed_json['original_url'])
        expect(yielded_response.path).to eq(parsed_json['path'])
        expect(yielded_response.wildcard).to eq(parsed_json['wildcard'])
        expect(yielded_response.status).to eq(parsed_json['status'])
        expect(yielded_response.method).to eq(parsed_json['method'])
        expect(yielded_response.content_length).to eq(parsed_json['content_length'])
        expect(yielded_response.line_count).to eq(parsed_json['line_count'])
        expect(yielded_response.word_count).to eq(parsed_json['word_count'])
        expect(yielded_response.headers).to eq(parsed_json['headers'])
        expect(yielded_response.extension).to eq(parsed_json['extension'])
      end
    end

    context "when the JSON \"type\" attribute is \"statistics\"" do
      let(:json) do
        "{\"type\":\"statistics\",\"timeouts\":0,\"requests\":18,\"expected_per_scan\":6,\"total_expected\":12,\"errors\":0,\"successes\":12,\"redirects\":0,\"client_errors\":6,\"server_errors\":0,\"total_scans\":2,\"initial_targets\":0,\"links_extracted\":0,\"extensions_collected\":0,\"status_200s\":12,\"status_301s\":0,\"status_302s\":0,\"status_401s\":0,\"status_403s\":0,\"status_429s\":0,\"status_500s\":0,\"status_503s\":0,\"status_504s\":0,\"status_508s\":0,\"wildcards_filtered\":0,\"responses_filtered\":0,\"resources_discovered\":4,\"url_format_errors\":0,\"redirection_errors\":0,\"connection_errors\":0,\"request_errors\":0,\"directory_scan_times\":[0.434531853,0.434228035],\"total_runtime\":[1.6527268240000001]}"
      end

      it "must parse each line and yield Feroxbuster::Statistics objects" do
        yielded_objects = []

        subject.parse(io) do |statistics|
          yielded_objects << statistics
        end

        expect(yielded_objects.length).to eq(1)
        expect(yielded_objects.first).to be_kind_of(Feroxbuster::Statistics)

        yielded_statistics = yielded_objects.first
        expect(yielded_statistics.timeouts).to eq(parsed_json['timeouts'])
        expect(yielded_statistics.requests).to eq(parsed_json['requests'])
        expect(yielded_statistics.expected_per_scan).to eq(parsed_json['expected_per_scan'])
        expect(yielded_statistics.total_expected).to eq(parsed_json['total_expected'])
        expect(yielded_statistics.errors).to eq(parsed_json['errors'])
        expect(yielded_statistics.successes).to eq(parsed_json['successes'])
        expect(yielded_statistics.redirects).to eq(parsed_json['redirects'])
        expect(yielded_statistics.client_errors).to eq(parsed_json['client_errors'])
        expect(yielded_statistics.server_errors).to eq(parsed_json['server_errors'])
        expect(yielded_statistics.total_scans).to eq(parsed_json['total_scans'])
        expect(yielded_statistics.initial_targets).to eq(parsed_json['initial_targets'])
        expect(yielded_statistics.links_extracted).to eq(parsed_json['links_extracted'])
        expect(yielded_statistics.extensions_collected).to eq(parsed_json['extensions_collected'])
        expect(yielded_statistics.status_200s).to eq(parsed_json['status_200s'])
        expect(yielded_statistics.status_301s).to eq(parsed_json['status_301s'])
        expect(yielded_statistics.status_302s).to eq(parsed_json['status_302s'])
        expect(yielded_statistics.status_401s).to eq(parsed_json['status_401s'])
        expect(yielded_statistics.status_403s).to eq(parsed_json['status_403s'])
        expect(yielded_statistics.status_429s).to eq(parsed_json['status_429s'])
        expect(yielded_statistics.status_500s).to eq(parsed_json['status_500s'])
        expect(yielded_statistics.status_503s).to eq(parsed_json['status_503s'])
        expect(yielded_statistics.status_504s).to eq(parsed_json['status_504s'])
        expect(yielded_statistics.status_508s).to eq(parsed_json['status_508s'])
        expect(yielded_statistics.wildcards_filtered).to eq(parsed_json['wildcards_filtered'])
        expect(yielded_statistics.responses_filtered).to eq(parsed_json['responses_filtered'])
        expect(yielded_statistics.resources_discovered).to eq(parsed_json['resources_discovered'])
        expect(yielded_statistics.url_format_errors).to eq(parsed_json['url_format_errors'])
        expect(yielded_statistics.redirection_errors).to eq(parsed_json['redirection_errors'])
        expect(yielded_statistics.connection_errors).to eq(parsed_json['connection_errors'])
        expect(yielded_statistics.request_errors).to eq(parsed_json['request_errors'])
        expect(yielded_statistics.directory_scan_times).to eq(parsed_json['directory_scan_times'])
        expect(yielded_statistics.total_runtime).to eq(parsed_json['total_runtime'])
      end
    end
  end
end