Skip to content

Commit

Permalink
handle output
Browse files Browse the repository at this point in the history
  • Loading branch information
dmdhrumilmistry committed Jul 14, 2024
1 parent cda96f2 commit 5331d25
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ scan-vulns:
docker: dbuild scan-vulns

run:
@go run ./cmd/offat/main.go
@go run -race ./cmd/offat/main.go

build:
@go build -o bin/offat cmd/offat/main.go

test:
@go test -v ./...
@go test -cover -v ./...

bump:
@go get -u ./...
Expand Down
18 changes: 16 additions & 2 deletions src/cmd/offat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/OWASP/OFFAT/src/pkg/parser"
"github.com/OWASP/OFFAT/src/pkg/tgen"
"github.com/OWASP/OFFAT/src/pkg/utils"
"github.com/OWASP/OFFAT/src/report"
"github.com/rs/zerolog/log"
)

Expand All @@ -26,6 +27,9 @@ type CliConfig struct {
// HTTP
RequestsPerSecond *int
SkipTlsVerfication *bool

// Report
OutputFilePath *string
}

func main() {
Expand All @@ -44,6 +48,8 @@ func main() {
config.RequestsPerSecond = flag.Int("r", 60, "number of requests per second")
config.SkipTlsVerfication = flag.Bool("ns", false, "disable TLS/SSL Verfication")

config.OutputFilePath = flag.String("o", "output.json", "JSON report output file path. default: output.json")

flag.Parse()

// Start Timer
Expand Down Expand Up @@ -78,7 +84,6 @@ func main() {
if err != nil {
log.Error().Err(err).Msg("failed to set baseUrl")
}
log.Print(parser.Doc.GetBaseUrl())

if err := parser.Doc.SetDocHttpParams(); err != nil {
log.Error().Stack().Err(err).Msg("failed while fetching doc http params")
Expand Down Expand Up @@ -108,8 +113,17 @@ func main() {
apiTests := apiTestHandler.GenerateTests()

apiTestHandler.RunApiTests(hc, client, apiTests)
log.Info().Msgf("Total Requests: %d", len(apiTests))

log.Info().Msgf("Generating and writing report to output file: %v", *config.OutputFilePath)
reportData, err := report.Report(apiTests, report.JSON)
if err != nil {
log.Error().Err(err).Msg("failed to generate report")
}

log.Info().Msgf("Total Requests: %d", len(hc.Requests))
if err := utils.WriteFile(*config.OutputFilePath, reportData); err != nil {
log.Error().Stack().Err(err).Msgf("failed to write json output file %v", *config.OutputFilePath)
}

elapsed := time.Since(now)
log.Info().Msgf("Time: %v", elapsed)
Expand Down
6 changes: 3 additions & 3 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ go 1.22.4
require (
github.com/dmdhrumilmistry/fasthttpclient v1.2.0
github.com/getkin/kin-openapi v0.126.0
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213
github.com/rs/zerolog v1.33.0
github.com/schollz/progressbar/v3 v3.14.4
github.com/valyala/fasthttp v1.55.0
golang.org/x/term v0.22.0
gopkg.in/yaml.v2 v2.4.0
)

Expand All @@ -19,7 +22,6 @@ require (
github.com/go-resty/resty/v2 v2.13.1 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/li-jin-gou/http2curl v0.1.2 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
Expand All @@ -30,11 +32,9 @@ require (
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/schollz/progressbar/v3 v3.14.4 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/time v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
16 changes: 9 additions & 7 deletions src/pkg/tgen/struct.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package tgen

import "github.com/dmdhrumilmistry/fasthttpclient/client"
import (
"github.com/dmdhrumilmistry/fasthttpclient/client"
)

// Holds data related for API testing
type ApiTests struct {
type ApiTest struct {
// Fields to be populated before making HTTP request
TestName string
Request *client.Request
TestName string `json:"test_name"`
Request *client.Request `json:"request"`

// Fields to be populated after making HTTP request
IsVulnerable bool
IsDataLeak bool
Response *client.Response
IsVulnerable bool `json:"is_vulnerable"`
IsDataLeak bool `json:"is_data_leak"`
Response *client.ConcurrentResponse `json:"response"`
}
54 changes: 46 additions & 8 deletions src/pkg/tgen/tgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package tgen

import (
"fmt"
"os"
"sync"

"github.com/k0kubun/go-ansi"
"github.com/schollz/progressbar/v3"
"golang.org/x/term"

"github.com/OWASP/OFFAT/src/pkg/http"
_ "github.com/OWASP/OFFAT/src/pkg/logging"
Expand All @@ -18,8 +24,8 @@ type TGenHandler struct {
DefaultHeaders map[string]string
}

func (t *TGenHandler) GenerateTests() []*ApiTests {
tests := []*ApiTests{}
func (t *TGenHandler) GenerateTests() []*ApiTest {
tests := []*ApiTest{}
if t.RunUnrestrictedHttpMethodTest {
newTests := UnrestrictedHttpMethods(t.Doc, t.DefaultQueryParams, t.DefaultHeaders)
tests = append(tests, newTests...)
Expand All @@ -30,13 +36,45 @@ func (t *TGenHandler) GenerateTests() []*ApiTests {
return tests
}

func (t *TGenHandler) RunApiTests(hc *http.Http, client c.ClientInterface, apiTests []*ApiTests) {
// generate requests
func (t *TGenHandler) RunApiTests(hc *http.Http, client c.ClientInterface, apiTests []*ApiTest) {
var wg sync.WaitGroup

// Get the terminal size
width, _, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil {
width = 80 // Default width
}

// Adjust the progress bar width based on terminal size
barWidth := width - 40 // Subtract 40 to account for other UI elements

bar := progressbar.NewOptions(
len(apiTests),
progressbar.OptionSetWriter(ansi.NewAnsiStdout()),
progressbar.OptionEnableColorCodes(true),
progressbar.OptionSetWidth(barWidth),
progressbar.OptionSetTheme(
progressbar.Theme{
Saucer: "[green]█[reset]",
SaucerHead: "[green]▓[reset]",
SaucerPadding: "░",
BarStart: "╢",
BarEnd: "╟",
},
),
)

for _, apiTest := range apiTests {
hc.Requests = append(hc.Requests, apiTest.Request)
wg.Add(1)
go func(apiTest *ApiTest) {
defer wg.Done()
defer bar.Add(1)

resp, err := client.Do(apiTest.Request.Uri, apiTest.Request.Method, apiTest.Request.QueryParams, apiTest.Request.Headers, apiTest.Request.Body)
apiTest.Response = c.NewConcurrentResponse(resp, err)
}(apiTest)
}

// make requests to the server concurrently
hc.Responses = c.MakeConcurrentRequests(hc.Requests, client)
fmt.Print("\n")
wg.Wait()
fmt.Println()
}
8 changes: 3 additions & 5 deletions src/pkg/tgen/unrestrictedmethods.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
)

// returns a new map with k:parser.DocHttpParams.Name, v:parser.DocHttpParams.Value
func UnrestrictedHttpMethods(docParams []*parser.DocHttpParams, queryParams any, headers any) []*ApiTests {
var tests []*ApiTests
func UnrestrictedHttpMethods(docParams []*parser.DocHttpParams, queryParams any, headers any) []*ApiTest {
var tests []*ApiTest
testName := "Unrestricted HTTP Methods/Verbs"

for _, docParam := range docParams {
Expand All @@ -22,16 +22,14 @@ func UnrestrictedHttpMethods(docParams []*parser.DocHttpParams, queryParams any,
jsonData, err := json.Marshal(bodyMap)
if err != nil {
log.Error().Stack().Err(err).Msg("failed to convert bodyMap to JSON")
} else {
jsonData = nil
}

// TODO: negate HTTP methods and add it to requests
// currently below request is only for testing purpose
log.Print(docParam.Url)
request := c.NewRequest(docParam.Url, docParam.HttpMethod, queryParams, headers, jsonData)

test := ApiTests{
test := ApiTest{
TestName: testName,
Request: request,
}
Expand Down
37 changes: 37 additions & 0 deletions src/pkg/utils/writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package utils

import (
"os"
"path/filepath"
)

func CreateDir(dirPath string) error {
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
// Directory does not exist, so create it
err := os.Mkdir(dirPath, 0755) // 0755 is the Unix permission mode
if err != nil {
return err
}
}

return nil
}

func WriteFile(filePath string, data []byte) error {
dirPath := filepath.Dir(filePath)
if err := CreateDir(dirPath); err != nil {
return err
}

file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
return err
}
defer file.Close()

if _, err := file.Write(data); err != nil {
return err
}

return nil
}
19 changes: 19 additions & 0 deletions src/report/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package report

import (
"encoding/json"
"fmt"

"github.com/OWASP/OFFAT/src/pkg/tgen"
)

const JSON = "json"

func Report(apiTests []*tgen.ApiTest, contentType string) ([]byte, error) {
switch contentType {
case JSON:
return json.Marshal(&apiTests)
default:
return nil, fmt.Errorf("invalid report content type")
}
}

0 comments on commit 5331d25

Please sign in to comment.