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

Add HAR support #257

Merged
merged 1 commit into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
| OAS 3.0 | JSON | ✅ | ✅ | ✅ | ✅ | X |
| OAS 3.0 | YAML | ✅ | ✅ | ✅ | ✅ | X |
| RAML | YAML | ✅ | ✅ | ✅ | ✅ | X |
| HAR | JSON | ✅ | ✅ | ✅ | ✅ | X |

## Installation
### Homebrew (macOS)
Expand Down
4 changes: 4 additions & 0 deletions shard.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ shards:
git: https://github.com/mamantoha/crest.git
version: 1.3.11

har:
git: https://github.com/neuralegion/har.git
version: 1.2.0

http-client-digest_auth:
git: https://github.com/mamantoha/http-client-digest_auth.git
version: 0.6.0
Expand Down
2 changes: 2 additions & 0 deletions shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ targets:
dependencies:
crest:
github: mamantoha/crest
har:
github: NeuraLegion/har

crystal: 1.8.2

Expand Down
153 changes: 153 additions & 0 deletions spec/functional_test/fixtures/har/example.har
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"log": {
"version": "1.2",
"creator": {
"name": "Firefox",
"version": "123.0.1"
},
"browser": {
"name": "Firefox",
"version": "123.0.1"
},
"pages": [
{
"id": "page_1",
"pageTimings": {
"onContentLoad": -1,
"onLoad": -1
},
"startedDateTime": "2024-03-17T00:17:31.653+09:00",
"title": "https://www.hahwul.com/"
}
],
"entries": [
{
"startedDateTime": "2024-03-17T00:17:31.653+09:00",
"request": {
"bodySize": 0,
"method": "GET",
"url": "https://www.hahwul.com/",
"httpVersion": "HTTP/2",
"headers": [
{
"name": "Host",
"value": "www.hahwul.com"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0"
},
{
"name": "Accept",
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
}
],
"cookies": [
{
"name": "_ga",
"value": "GA1.1.1310623768.1671977578"
},
{
"name": "_ga_N9SYSZ280B",
"value": "GS1.1.1710602187.53.0.1710602187.0.0.0"
}
],
"queryString": [],
"headersSize": 690
},
"response": {
"status": 304,
"statusText": "",
"httpVersion": "HTTP/2",
"headers": [
{
"name": "date",
"value": "Sat, 16 Mar 2024 15:17:31 GMT"
},
{
"name": "via",
"value": "1.1 varnish"
},
{
"name": "cache-control",
"value": "max-age=600"
},
{
"name": "etag",
"value": "W/\"65f5937e-aadc\""
},
{
"name": "expires",
"value": "Sat, 16 Mar 2024 13:59:28 GMT"
},
{
"name": "age",
"value": "258"
},
{
"name": "x-served-by",
"value": "cache-icn1450027-ICN"
},
{
"name": "x-cache",
"value": "HIT"
},
{
"name": "x-cache-hits",
"value": "3"
},
{
"name": "x-timer",
"value": "S1710602252.651544,VS0,VE1"
},
{
"name": "vary",
"value": "Accept-Encoding"
},
{
"name": "x-fastly-request-id",
"value": "f40aba25bfeacafe5eb1bce11729a353344a30c3"
},
{
"name": "X-Firefox-Spdy",
"value": "h2"
}
],
"cookies": [],
"content": {
"mimeType": "text/plain",
"size": 0,
"text": "<!DOCTYPE html>\n<html lang=\"en-US\" class=\"no-js\">\n......\n\n</html>\n"
},
"redirectURL": "",
"headersSize": 382,
"bodySize": 382
},
"cache": {
"afterRequest": {
"expires": "1709911195",
"lastFetched": "1710602189",
"fetchCount": "2",
"_dataSize": "9437",
"_lastModified": "1710602191",
"_device": ""
}
},
"timings": {
"blocked": 0,
"dns": 0,
"connect": 0,
"ssl": 0,
"send": 0,
"wait": 7,
"receive": 0
},
"time": 7,
"_securityState": "secure",
"serverIPAddress": "185.199.108.153",
"connection": "443",
"pageref": "page_1"
}
]
}
}
4 changes: 4 additions & 0 deletions spec/functional_test/func_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,8 @@ class FunctionalTester
def app
@app
end

def set_url(url)
@app.options[:url] = url
end
end
19 changes: 19 additions & 0 deletions spec/functional_test/testers/har_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require "../func_spec.cr"

extected_endpoints = [
Endpoint.new("https://www.hahwul.com/", "GET", [
Param.new("Host", "www.hahwul.com", "header"),
Param.new("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0", "header"),
Param.new("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "header"),
Param.new("_ga", "GA1.1.1310623768.1671977578", "cookie"),
Param.new("_ga_N9SYSZ280B", "GS1.1.1710602187.53.0.1710602187.0.0.0", "cookie"),
]),
]

instance = FunctionalTester.new("fixtures/har/", {
:techs => 1,
:endpoints => 1,
}, extected_endpoints)

instance.set_url "https://www.hahwul.com"
instance.test_all
1 change: 1 addition & 0 deletions src/analyzer/analyzer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def initialize_analyzers(logger : NoirLogger)
analyzers["go_echo"] = ->analyzer_go_echo(Hash(Symbol, String))
analyzers["go_fiber"] = ->analyzer_go_fiber(Hash(Symbol, String))
analyzers["go_gin"] = ->analyzer_go_gin(Hash(Symbol, String))
analyzers["har"] = ->analyzer_har(Hash(Symbol, String))
analyzers["java_armeria"] = ->analyzer_armeria(Hash(Symbol, String))
analyzers["java_jsp"] = ->analyzer_jsp(Hash(Symbol, String))
analyzers["java_spring"] = ->analyzer_java_spring(Hash(Symbol, String))
Expand Down
68 changes: 68 additions & 0 deletions src/analyzer/analyzers/analyzer_har.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require "../../models/analyzer"

class AnalyzerHar < Analyzer
def analyze
locator = CodeLocator.instance
har_files = locator.all("har-path")

if har_files.is_a?(Array(String)) && @url != ""
har_files.each do |har_file|
if File.exists?(har_file)
data = HAR.from_file(har_file)
logger.debug "Open #{har_file} file"
data.entries.each do |entry|
if entry.request.url.includes? @url
path = entry.request.url.to_s.gsub(@url, "")
endpoint = Endpoint.new(path, entry.request.method)

entry.request.query_string.each do |query|
endpoint.params << Param.new(query.name, query.value, "query")
end

is_websocket = false
entry.request.headers.each do |header|
endpoint.params << Param.new(header.name, header.value, "header")
if header.name == "Upgrade" && header.value == "websocket"
is_websocket = true
end
end

entry.request.cookies.each do |cookie|
endpoint.params << Param.new(cookie.name, cookie.value, "cookie")
end

post_data = entry.request.post_data
if post_data
params = post_data.params
mime_type = post_data.mime_type
param_type = "body"
if mime_type == "application/json"
param_type = "json"
end
if params
params.each do |param|
endpoint.params << Param.new(param.name, param.value.to_s, param_type)
end
end
end

details = Details.new(PathInfo.new(har_file, 0))
endpoint.set_details(details)
if is_websocket
endpoint.set_protocol "ws"
end
@result << endpoint
end
end
end
end
end

@result
end
end

def analyzer_har(options : Hash(Symbol, String))
instance = AnalyzerHar.new(options)
instance.analyze
end
1 change: 1 addition & 0 deletions src/detector/detector.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def detect_techs(base_path : String, options : Hash(Symbol, String), logger : No
DetectorGoEcho,
DetectorGoFiber,
DetectorGoGin,
DetectorHar,
DetectorJavaArmeria,
DetectorJavaJsp,
DetectorJavaSpring,
Expand Down
29 changes: 29 additions & 0 deletions src/detector/detectors/har.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require "../../models/detector"
require "../../utils/json"
require "../../utils/yaml"
require "../../models/code_locator"
require "har"

class DetectorHar < Detector
def detect(filename : String, file_contents : String) : Bool
if (filename.includes? ".har") || (filename.includes? ".json")
if valid_json? file_contents
begin
data = HAR.from_string(file_contents)
if data.version.to_s.includes? "1."
locator = CodeLocator.instance
locator.push("har-path", filename)
return true
end
rescue
end
end
end

false
end

def set_name
@name = "har"
end
end
9 changes: 9 additions & 0 deletions src/techs/techs.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ module NoirTechs
:language => "Crystal",
:similar => ["kemal", "crystal-kemal", "crystal_kemal"],
},
:crystal_lucky => {
:framework => "Lucky",
:language => "Crystal",
:similar => ["lucky", "crystal-lucky", "crystal_lucky"],
},
:cs_aspnet_mvc => {
:framework => "ASP.NET MVC",
:language => "C#",
Expand All @@ -30,6 +35,10 @@ module NoirTechs
:language => "Go",
:similar => ["gin", "go-gin", "go_gin"],
},
:har => {
:format => ["JSON"],
:similar => ["har"],
},
:java_armeria => {
:framework => "Armeria",
:language => "Java",
Expand Down
Loading