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

Sync #28

Merged
merged 23 commits into from
Aug 19, 2023
Merged

Sync #28

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
2 changes: 1 addition & 1 deletion .ameba.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Metrics/CyclomaticComplexity:
Description: Disallows methods with a cyclomatic complexity higher than `MaxComplexity`
MaxComplexity: 25
MaxComplexity: 30
Enabled: true
Severity: Warning
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@
| JS | Express | ✅ | ✅ | X | X | X |
| JS | Next | X | X | X | X | X |

### Specification

| Specification | Format | URL | Method | Param | Header | WS |
|------------------------|---------|-----|--------|-------|--------|----|
| OAS 2.0 (Swagger 2.0) | JSON | ✅ | ✅ | ✅ | ✅ | X |
| OAS 2.0 (Swagger 2.0) | YAML | ✅ | ✅ | ✅ | ✅ | X |
| OAS 3.0 | JSON | ✅ | ✅ | ✅ | ✅ | X |
| OAS 3.0 | YAML | ✅ | ✅ | ✅ | ✅ | X |

## Installation
### Homebrew (macOS)
```bash
Expand Down
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: noir
version: 0.3.0
version: 0.4.0

authors:
- hahwul <[email protected]>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
require "../../src/detector/detectors/*"
require "../../src/models/code_locator"

describe "Detect Swagger Docs" do
describe "Detect OAS 2.0(Swagger) Docs" do
options = default_options()
instance = DetectorSwagger.new options
instance = DetectorOas2.new options

it "json format" do
content = <<-EOS
Expand Down
40 changes: 40 additions & 0 deletions spec/detector/detect_oas3_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "../../src/detector/detectors/*"
require "../../src/models/code_locator"

describe "Detect OAS 3.0 Docs" do
options = default_options()
instance = DetectorOas3.new options

it "json format" do
content = <<-EOS
{
"openapi": "3.0.0",
"info": "test"
}
EOS

instance.detect("docs.json", content).should eq(true)
end
it "yaml format" do
content = <<-EOS
openapi: 3.0.0
info:
version: 1.0.0
EOS

instance.detect("docs.yml", content).should eq(true)
end

it "code_locator" do
content = <<-EOS
{
"openapi": "3.0.0",
"info": "test"
}
EOS

instance.detect("docs.json", content)
locator = CodeLocator.instance
locator.get("oas3-json").should eq("docs.json")
end
end
3 changes: 2 additions & 1 deletion src/analyzer/analyzer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ def initialize_analyzers(logger : NoirLogger)
analyzers["python_django"] = ->analyzer_django(Hash(Symbol, String))
analyzers["js_express"] = ->analyzer_express(Hash(Symbol, String))
analyzers["crystal_kemal"] = ->analyzer_kemal(Hash(Symbol, String))
analyzers["swagger"] = ->analyzer_swagger(Hash(Symbol, String))
analyzers["oas2"] = ->analyzer_oas2(Hash(Symbol, String))
analyzers["oas3"] = ->analyzer_oas3(Hash(Symbol, String))

logger.info_sub "#{analyzers.size} Analyzers initialized"
logger.debug "Analyzers:"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "../../models/analyzer"

class AnalyzerSwagger < Analyzer
class AnalyzerOAS2 < Analyzer
def analyze
locator = CodeLocator.instance
swagger_json = locator.get("swagger-json")
Expand All @@ -10,28 +10,34 @@ class AnalyzerSwagger < Analyzer
if File.exists?(swagger_json)
content = File.read(swagger_json, encoding: "utf-8", invalid: :skip)
json_obj = JSON.parse(content)
base_path = @url
if json_obj["basePath"].to_s != ""
base_path = base_path + json_obj["basePath"].to_s
end
json_obj["paths"].as_h.each do |path, path_obj|
path_obj.as_h.each do |method, method_obj|
params_query = [] of Param
params_body = [] of Param
params = [] of Param

if method_obj.as_h.has_key?("parameters")
method_obj["parameters"].as_a.each do |param_obj|
param_name = param_obj["name"].to_s
if param_obj["in"] == "query"
param = Param.new(param_name, "", "query")
params_query << param
params << param
elsif param_obj["in"] == "body"
param = Param.new(param_name, "", "json")
params_body << param
params << param
elsif param_obj["in"] == "formData"
param = Param.new(param_name, "", "form")
params_body << param
params << param
elsif param_obj["in"] == "header"
param = Param.new(param_name, "", "header")
params << param
end
end
@result << Endpoint.new(path, method.upcase, params_body)
@result << Endpoint.new(base_path + path, method.upcase, params)
else
@result << Endpoint.new(path, method.upcase)
@result << Endpoint.new(base_path + path, method.upcase)
end
end
end
Expand All @@ -42,28 +48,34 @@ class AnalyzerSwagger < Analyzer
if File.exists?(swagger_yaml)
content = File.read(swagger_yaml, encoding: "utf-8", invalid: :skip)
yaml_obj = YAML.parse(content)
base_path = @url
if yaml_obj["basePath"].to_s != ""
base_path = base_path + yaml_obj["basePath"].to_s
end
yaml_obj["paths"].as_h.each do |path, path_obj|
path_obj.as_h.each do |method, method_obj|
params_query = [] of Param
params_body = [] of Param
params = [] of Param

if method_obj.as_h.has_key?("parameters")
method_obj["parameters"].as_a.each do |param_obj|
param_name = param_obj["name"].to_s
if param_obj["in"] == "query"
param = Param.new(param_name, "", "query")
params_query << param
params << param
elsif param_obj["in"] == "body"
param = Param.new(param_name, "", "json")
params_body << param
params << param
elsif param_obj["in"] == "formData"
param = Param.new(param_name, "", "form")
params_body << param
params << param
elsif param_obj["in"] == "header"
param = Param.new(param_name, "", "header")
params << param
end
end
@result << Endpoint.new(path.to_s, method.to_s.upcase, params_body)
@result << Endpoint.new(base_path + path.to_s, method.to_s.upcase, params)
else
@result << Endpoint.new(path.to_s, method.to_s.upcase)
@result << Endpoint.new(base_path + path.to_s, method.to_s.upcase)
end
end
end
Expand All @@ -74,7 +86,7 @@ class AnalyzerSwagger < Analyzer
end
end

def analyzer_swagger(options : Hash(Symbol, String))
instance = AnalyzerSwagger.new(options)
def analyzer_oas2(options : Hash(Symbol, String))
instance = AnalyzerOAS2.new(options)
instance.analyze
end
143 changes: 143 additions & 0 deletions src/analyzer/analyzers/analyzer_oas3.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
require "../../models/analyzer"

class AnalyzerOAS3 < Analyzer
def get_base_path(servers)
base_path = @url
servers.as_a.each do |server_obj|
if server_obj["url"].to_s.starts_with?("http")
user_uri = URI.parse(@url)
source_uri = URI.parse(server_obj["url"].to_s)
if user_uri.host == source_uri.host
base_path = @url + source_uri.path
break
end
end
end

base_path
end

def analyze
locator = CodeLocator.instance
oas3_json = locator.get("oas3-json")
oas3_yaml = locator.get("oas3-yaml")

if !oas3_json.nil?
if File.exists?(oas3_json)
content = File.read(oas3_json, encoding: "utf-8", invalid: :skip)
json_obj = JSON.parse(content)

base_path = get_base_path json_obj["servers"]

json_obj["paths"].as_h.each do |path, path_obj|
path_obj.as_h.each do |method, method_obj|
params = [] of Param

if method_obj.is_a?(JSON::Any) && method_obj.is_a?(Hash(String, JSON::Any))
if method_obj.as_h.has_key?("parameters")
method_obj["parameters"].as_a.each do |param_obj|
param_name = param_obj["name"].to_s
if param_obj["in"] == "query"
param = Param.new(param_name, "", "query")
params << param
elsif param_obj["in"] == "header"
param = Param.new(param_name, "", "header")
params << param
end
end
end

if method_obj.as_h.has_key?("requestBody")
method_obj["requestBody"]["content"].as_h.each do |content_type, content_obj|
if content_type == "application/json"
content_obj["schema"]["properties"].as_h.each do |param_name, _|
param = Param.new(param_name, "", "json")
params << param
end
elsif content_type == "application/x-www-form-urlencoded"
content_obj["schema"]["properties"].as_h.each do |param_name, _|
param = Param.new(param_name, "", "form")
params << param
end
end
end
end
end

if params.size > 0 && params.size > 0
@result << Endpoint.new(base_path + path, method.upcase, params + params)
elsif params.size > 0
@result << Endpoint.new(base_path + path, method.upcase, params)
elsif params.size > 0
@result << Endpoint.new(base_path + path, method.upcase, params)
else
@result << Endpoint.new(base_path + path, method.upcase)
end
end
end
end
end

if !oas3_yaml.nil?
if File.exists?(oas3_yaml)
content = File.read(oas3_yaml, encoding: "utf-8", invalid: :skip)
yaml_obj = YAML.parse(content)
base_path = get_base_path yaml_obj["servers"]

yaml_obj["paths"].as_h.each do |path, path_obj|
path_obj.as_h.each do |method, method_obj|
params = [] of Param

if method_obj.is_a?(YAML::Any) && method_obj.is_a?(Hash(String, YAML::Any))
if method_obj.as_h.has_key?("parameters")
method_obj["parameters"].as_a.each do |param_obj|
param_name = param_obj["name"].to_s
if param_obj["in"] == "query"
param = Param.new(param_name, "", "query")
params << param
elsif param_obj["in"] == "header"
param = Param.new(param_name, "", "header")
params << param
end
end
end

if method_obj.as_h.has_key?("requestBody")
method_obj["requestBody"]["content"].as_h.each do |content_type, content_obj|
if content_type == "application/json"
content_obj["schema"]["properties"].as_h.each do |param_name, _|
param = Param.new(param_name.to_s, "", "json")
params << param
end
elsif content_type == "application/x-www-form-urlencoded"
content_obj["schema"]["properties"].as_h.each do |param_name, _|
param = Param.new(param_name.to_s, "", "form")
params << param
end
end
end
end
end

if params.size > 0 && params.size > 0
@result << Endpoint.new(base_path + path.to_s, method.to_s.upcase, params + params)
elsif params.size > 0
@result << Endpoint.new(base_path + path.to_s, method.to_s.upcase, params)
elsif params.size > 0
@result << Endpoint.new(base_path + path.to_s, method.to_s.upcase, params)
else
@result << Endpoint.new(base_path + path.to_s, method.to_s.upcase)
end
end
end
end
end

@result
end
end

def analyzer_oas3(options : Hash(Symbol, String))
instance = AnalyzerOAS3.new(options)
instance.analyze
end
2 changes: 1 addition & 1 deletion src/detector/detector.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def detect_techs(base_path : String, options : Hash(Symbol, String))
defind_detectors([
DetectorCrystalKemal, DetectorGoEcho, DetectorJavaJsp, DetectorJavaSpring,
DetectorJsExpress, DetectorPhpPure, DetectorPythonDjango, DetectorPythonFlask,
DetectorRubyRails, DetectorRubySinatra, DetectorSwagger,
DetectorRubyRails, DetectorRubySinatra, DetectorOas2, DetectorOas3,
])
Dir.glob("#{base_path}/**/*") do |file|
spawn do
Expand Down
37 changes: 37 additions & 0 deletions src/detector/detectors/oas2.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require "../../models/detector"
require "../../utils/json"
require "../../utils/yaml"
require "../../models/code_locator"

class DetectorOas2 < Detector
def detect(filename : String, file_contents : String) : Bool
check = false
if valid_json? file_contents
data = JSON.parse(file_contents)
begin
if data["swagger"].as_s.includes? "2."
check = true
locator = CodeLocator.instance
locator.set("swagger-json", filename)
end
rescue
end
elsif valid_yaml? file_contents
data = YAML.parse(file_contents)
begin
if data["swagger"].as_s.includes? "2."
check = true
locator = CodeLocator.instance
locator.set("swagger-yaml", filename)
end
rescue
end
end

check
end

def set_name
@name = "oas2"
end
end
Loading
Loading