From 360bf84d2af244c7926c2aa7c1f23d0c280b8238 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Sun, 21 Mar 2021 22:23:27 +0700 Subject: all: implement interface and API to delete attack result When user clicking the "Delete" button on attack result item, it will send request to API to delete the selected item. Server will check if the requested result file name exist and delete them from file system if its exist, otherwise it will return 404 Not found. --- _www/index.css | 27 +++++++++++- _www/index.html | 3 ++ _www/index.js | 124 ++++++++++++++++++++++++++++++++++++++++++-------------- http_target.go | 7 ++++ trunks.go | 79 ++++++++++++++++++++++++------------ 5 files changed, 182 insertions(+), 58 deletions(-) diff --git a/_www/index.css b/_www/index.css index ef43f5b..f80f449 100644 --- a/_www/index.css +++ b/_www/index.css @@ -3,6 +3,28 @@ body { margin: 0; padding: 0; } + +#notif, +#notif-error { + position: absolute; + top: 1em; + left: 10%; + width: 80%; +} + +#notif > div { + background-color: white; + border: 1px solid lightgrey; + margin: 1em auto; + padding: 1em; +} + +#notif-error > div { + background-color: salmon; + margin: 1em auto; + padding: 1em; +} + .nav { height: calc(100% - 2em); padding: 1em; @@ -41,8 +63,11 @@ body { } .mono { background-color: cornsilk; - border-radius: 1em; font-family: monospace; padding: 1em; overflow: auto; } + +.results > .result-name { + margin-bottom: 1em; +} diff --git a/_www/index.html b/_www/index.html index 97ac64d..56754af 100644 --- a/_www/index.html +++ b/_www/index.html @@ -26,5 +26,8 @@
+ +
+
diff --git a/_www/index.js b/_www/index.js index 642a7bc..044bd57 100644 --- a/_www/index.js +++ b/_www/index.js @@ -127,7 +127,7 @@ function renderTarget(targetID) { let http = target.HttpTargets[x] w += ` -
+

${http.Name}

-
+ +
${_requestMethods[http.Method]} ${http.Path}
Content-Type: ${_requestTypes[http.RequestType]}
+ +

Headers

+
+ +

Parameters

+
+ +

Run response

+

+
+				

Attack results

+
+
` + } + w += "
" + + document.getElementById("main-content").innerHTML = w + + for (let x = 0; x < target.HttpTargets.length; x++) { + let http = target.HttpTargets[x] if (Object.keys(http.Headers).length > 0) { - w += "

Headers

" - w += renderHttpTargetHeaders(target, http) + renderHttpTargetHeaders(target, http) } if (Object.keys(http.Params).length > 0) { - w += "

Parameters

" - w += renderHttpTargetParams(target, http) + renderHttpTargetParams(target, http) } - w += ` -

Run response

-
-			
- ` - if (http.Results && Object.keys(http.Results).length > 0) { - w += "

Attack results

" - w += renderHttpAttackResults(target, http) + renderHttpAttackResults(target, http) } - - w += "
" } - w += "" - - document.getElementById("main-content").innerHTML = w } function renderHttpTargetHeaders(target, http) { - let w = `
` + let w = "" for (const k in http.Headers) { w += `
@@ -184,12 +191,11 @@ function renderHttpTargetHeaders(target, http) {
` } - w += "
" - return w + document.getElementById(`${http.ID}_headers`).innerHTML = w } function renderHttpTargetParams(target, http) { - let w = `
` + let w = "" for (const k in http.Params) { w += `
@@ -201,30 +207,33 @@ function renderHttpTargetParams(target, http) {
` } - w += "
" - return w + document.getElementById(`${http.ID}_params`).innerHTML = w } function renderHttpAttackResults(target, http) { - let w = `
` + let w = "" for (let x = 0; x < http.Results.length; x++) { let result = http.Results[x] w += ` -
-   --   ${result.Name} +   + +
` } - w += "
" - return w + document.getElementById(`${http.ID}_results`).innerHTML = w } async function run(targetID, httpTargetID) { @@ -273,7 +282,38 @@ async function attack(targetID, httpTargetID) { let res = await fres.json() } -async function getAttackResult(button, name) { +async function attackResultDelete(name) { + let url = "/_trunks/api/target/attack/result?name=" + name + let fres = await fetch(url, { + method: "DELETE", + }) + let res = await fres.json() + if (res.code != 200) { + console.log("attackResultDelete: ", res) + notifError(res.message) + return + } + + let ids = name.split(".") + let target = _targets[ids[0]] + if (!target) { + return + } + let httpTarget = getHttpTargetByID(target, ids[1]) + if (!httpTarget) { + return + } + for (let x = 0; x < httpTarget.Results.length; x++) { + let result = httpTarget.Results[x] + if (result.Name == name) { + httpTarget.Results.splice(x, 1) + renderHttpAttackResults(target, httpTarget) + return + } + } +} + +async function attackResultGet(button, name) { let el = document.getElementById(name) if (el.style.display === "block") { @@ -320,3 +360,25 @@ function onChangeHttpParam(targetID, httpTargetID, key, val) { let httpTarget = getHttpTargetByID(target, httpTargetID) httpTarget.Params[key] = val } + +function notif(msg) { + let root = document.getElementById("notif") + let item = document.createElement("div"); + item.innerHTML = msg + root.appendChild(item) + + setTimeout(function() { + root.removeChild(item) + }, 5000) +} + +function notifError(msg) { + let root = document.getElementById("notif-error") + let item = document.createElement("div"); + item.innerHTML = msg + root.appendChild(item) + + setTimeout(function() { + root.removeChild(item) + }, 5000) +} diff --git a/http_target.go b/http_target.go index 03c05b1..5652df3 100644 --- a/http_target.go +++ b/http_target.go @@ -5,10 +5,12 @@ package trunks import ( + "os" "path/filepath" "sync" libhttp "github.com/shuLhan/share/lib/http" + "github.com/shuLhan/share/lib/mlog" vegeta "github.com/tsenart/vegeta/v12/lib" ) @@ -87,6 +89,11 @@ func (ht *HttpTarget) deleteResult(result *AttackResult) { copy(ht.Results[x:], ht.Results[x+1:]) ht.Results[len(ht.Results)-1] = nil ht.Results = ht.Results[:len(ht.Results)-1] + + err := os.Remove(result.fullpath) + if err != nil { + mlog.Errf("deleteResult: %q: %s\n", result.fullpath, err) + } } func (ht *HttpTarget) addResult(dir, name string) (err error) { diff --git a/trunks.go b/trunks.go index df6f2c0..9b700e9 100644 --- a/trunks.go +++ b/trunks.go @@ -200,7 +200,7 @@ func (trunks *Trunks) registerHttpApis() (err error) { Path: apiTargetAttackResult, RequestType: libhttp.RequestTypeJSON, ResponseType: libhttp.ResponseTypeJSON, - Call: trunks.apiTargetAttackResultsDelete, + Call: trunks.apiTargetAttackResultDelete, }) if err != nil { return err @@ -302,32 +302,14 @@ func (trunks *Trunks) apiTargetAttackCancel(epr *libhttp.EndpointRequest) (resbo } func (trunks *Trunks) apiTargetAttackResultGet(epr *libhttp.EndpointRequest) (resbody []byte, err error) { - res := &libhttp.EndpointResponse{ - E: liberrors.E{ - Code: http.StatusNotFound, - Name: "ERR_ATTACK_RESULT_NOT_FOUND", - }, - } - name := epr.HttpRequest.Form.Get(paramNameName) if len(name) == 0 { return nil, errInvalidParameter(paramNameName, name) } - t, ht := trunks.getTargetByResultFilename(name) - if t == nil { - res.Message = "Target ID not found" - return nil, res - } - if ht == nil { - res.Message = "HttpTarget ID not found" - return nil, res - } - - result := ht.getResultByName(name) - if result == nil { - res.Message = "Result file not found" - return nil, res + _, _, result, err := trunks.getAttackResultByName(name) + if err != nil { + return nil, err } err = result.load() @@ -335,15 +317,33 @@ func (trunks *Trunks) apiTargetAttackResultGet(epr *libhttp.EndpointRequest) (re return nil, err } + res := libhttp.EndpointResponse{} res.Code = http.StatusOK - res.Name = "OK_TARGET_ATTACK_RESULT" + res.Name = "OK_TARGET_ATTACK_RESULT_GET" res.Data = result - return json.Marshal(res) + return json.Marshal(&res) } -func (trunks *Trunks) apiTargetAttackResultsDelete(epr *libhttp.EndpointRequest) (resbody []byte, err error) { - return resbody, nil +func (trunks *Trunks) apiTargetAttackResultDelete(epr *libhttp.EndpointRequest) (resbody []byte, err error) { + name := epr.HttpRequest.Form.Get(paramNameName) + if len(name) == 0 { + return nil, errInvalidParameter(paramNameName, name) + } + + _, ht, result, err := trunks.getAttackResultByName(name) + if err != nil { + return nil, err + } + + ht.deleteResult(result) + + res := libhttp.EndpointResponse{} + res.Code = http.StatusOK + res.Name = "OK_TARGET_ATTACK_RESULT_GET" + res.Data = result + + return json.Marshal(&res) } func (trunks *Trunks) apiTargetRun(epr *libhttp.EndpointRequest) ([]byte, error) { @@ -391,6 +391,33 @@ func (trunks *Trunks) getTargetByID(id string) *Target { return nil } +func (trunks *Trunks) getAttackResultByName(name string) (t *Target, ht *HttpTarget, result *AttackResult, err error) { + res := &libhttp.EndpointResponse{ + E: liberrors.E{ + Code: http.StatusNotFound, + Name: "ERR_ATTACK_RESULT_NOT_FOUND", + }, + } + + t, ht = trunks.getTargetByResultFilename(name) + if t == nil { + res.Message = "Target ID not found" + return nil, nil, nil, res + } + if ht == nil { + res.Message = "HttpTarget ID not found" + return nil, nil, nil, res + } + + result = ht.getResultByName(name) + if result == nil { + res.Message = "Result file not found" + return nil, nil, nil, res + } + + return t, ht, result, nil +} + func (trunks *Trunks) getTargetByResultFilename(name string) (t *Target, ht *HttpTarget) { names := strings.Split(name, ".") -- cgit v1.3