Skip to content

Commit

Permalink
Merge pull request #606 from akto-api-security/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
notshivansh authored Oct 3, 2023
2 parents e3f99ca + f25e35b commit 0ca1964
Show file tree
Hide file tree
Showing 25 changed files with 863 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.akto.action.testing_issues;

import com.akto.action.ExportSampleDataAction;
import com.akto.action.UserAction;
import com.akto.dao.demo.VulnerableRequestForTemplateDao;
import com.akto.dao.test_editor.YamlTemplateDao;
Expand All @@ -13,6 +14,7 @@
import com.akto.dto.test_editor.YamlTemplate;
import com.akto.dto.test_run_findings.TestingIssuesId;
import com.akto.dto.test_run_findings.TestingRunIssues;
import com.akto.dto.testing.TestResult;
import com.akto.dto.testing.TestingRunResult;
import com.akto.dto.testing.sources.TestSourceConfig;
import com.akto.util.enums.GlobalEnums;
Expand All @@ -26,6 +28,7 @@
import org.bson.conversions.Bson;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -37,6 +40,8 @@ public class IssuesAction extends UserAction {
private TestingIssuesId issueId;
private List<TestingIssuesId> issueIdArray;
private TestingRunResult testingRunResult;
private List<TestingRunResult> testingRunResults;
private Map<String, String> sampleDataVsCurlMap;
private TestRunIssueStatus statusToBeUpdated;
private String ignoreReason;
private int skip;
Expand Down Expand Up @@ -109,6 +114,46 @@ public String fetchAllIssues() {
}
return SUCCESS.toUpperCase();
}

public String fetchVulnerableTestingRunResultsFromIssues() {
Bson filters = createFilters();
try {
List<TestingRunIssues> issues = TestingRunIssuesDao.instance.findAll(filters, skip, 50, null);
this.totalIssuesCount = issues.size();
List<Bson> andFilters = new ArrayList<>();
for (TestingRunIssues issue : issues) {
andFilters.add(Filters.and(
Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, issue.getLatestTestingRunSummaryId()),
Filters.eq(TestingRunResult.TEST_SUB_TYPE, issue.getId().getTestSubCategory()),
Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()),
Filters.eq(TestingRunResult.VULNERABLE, true)
));
}
if (issues.isEmpty()) {
this.testingRunResults = new ArrayList<>();
this.sampleDataVsCurlMap = new HashMap<>();
return SUCCESS.toUpperCase();
}
Bson orFilters = Filters.or(andFilters);
this.testingRunResults = TestingRunResultDao.instance.findAll(orFilters);
Map<String, String> sampleDataVsCurlMap = new HashMap<>();
for (TestingRunResult runResult: this.testingRunResults) {
List<TestResult> testResults = new ArrayList<>();
for (TestResult testResult : runResult.getTestResults()) {
if (testResult.isVulnerable()) {
testResults.add(testResult);
sampleDataVsCurlMap.put(testResult.getMessage(), ExportSampleDataAction.getCurl(testResult.getMessage()));
sampleDataVsCurlMap.put(testResult.getOriginalMessage(), ExportSampleDataAction.getCurl(testResult.getOriginalMessage()));
}
}
runResult.setTestResults(testResults);
}
this.sampleDataVsCurlMap = sampleDataVsCurlMap;
} catch (Exception e) {
return ERROR.toUpperCase();
}
return SUCCESS.toUpperCase();
}
public String fetchTestingRunResult() {
if (issueId == null) {
throw new IllegalStateException();
Expand Down Expand Up @@ -378,4 +423,20 @@ public boolean getFetchOnlyActive() {
public void setFetchOnlyActive(boolean fetchOnlyActive) {
this.fetchOnlyActive = fetchOnlyActive;
}

public List<TestingRunResult> getTestingRunResults() {
return testingRunResults;
}

public void setTestingRunResults(List<TestingRunResult> testingRunResults) {
this.testingRunResults = testingRunResults;
}

public Map<String, String> getSampleDataVsCurlMap() {
return sampleDataVsCurlMap;
}

public void setSampleDataVsCurlMap(Map<String, String> sampleDataVsCurlMap) {
this.sampleDataVsCurlMap = sampleDataVsCurlMap;
}
}
9 changes: 9 additions & 0 deletions apps/dashboard/src/main/resources/struts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,15 @@
</result>
</action>

<action name="api/fetchVulnerableTestingRunResultsFromIssues" class="com.akto.action.testing_issues.IssuesAction" method="fetchVulnerableTestingRunResultsFromIssues">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
<result name="SUCCESS" type="json"/>
<result name="ERROR" type="httpheader">
<param name="status">401</param>
</result>
</action>

<action name="api/fetchAffectedEndpoints" class="com.akto.action.testing_issues.IssuesAction" method="fetchAffectedEndpoints">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ function AktoGptLayout({prompts,closeModal, runCustomTests}) {
<Box padding="5" maxWidth="65vw" background="bg-subdued">
<div className="response-message">
<HorizontalStack gap="6" align="start">
<Avatar name="Akto" source='akto_colored.svg' size="medium"/>
<Avatar name="Akto" source='/public/akto_colored.svg' size="medium"/>
{loading ? <Spinner size="small" />
: <ResponseComponent response={func.getResponse(response,queryType)} chatLogRef={chatLogRef} onCompletion={() => handleCompletion()}/>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function highlightPaths(highlightPathMap, ref){
}

function highlightHeaders(data, ref, getLineNumbers){
const diffRange = []
let diffRange = []
const headerKeysMap = data.headersMap

// add classname for first line only
Expand Down Expand Up @@ -93,7 +93,7 @@ function highlightHeaders(data, ref, getLineNumbers){
diffRange.sort((a,b) => a.range - b.range)
let currentRange = null
let result = []

diffRange = Array.from(new Set(diffRange.map(i => JSON.stringify(i))), JSON.parse);
for (const obj of diffRange) {
if (!currentRange) {
currentRange = { start: obj.range, end: obj.range, key: obj.key };
Expand All @@ -113,7 +113,8 @@ function highlightHeaders(data, ref, getLineNumbers){
ref.createDecorationsCollection([{
range: new monaco.Range(obj.start, 1, obj.end, 100),
options: {
blockClassName: className
blockClassName: className,
isWholeLine: true
}
}])
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ function SampleDataComponent(props) {
})
}
if (data.originalMessage) {
if(items.length==2){
items[0].content = "Copy attempt request as curl"
items[1].content = "Copy attempt request as burp"
}
items.push({
content: 'Copy original request as curl',
onAction: () => { copyRequest(type, "CURL", data.originalMessage) },
Expand All @@ -170,6 +174,9 @@ function SampleDataComponent(props) {
})
}
if (data.originalMessage) {
if(items.length==1){
items[0].content = "Copy attempt response"
}
items.push({
content: 'Copy original response',
onAction: () => { copyRequest(type, "RESPONSE", data.originalMessage) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ const transform = {
}
},

processArrayJson(input) {
try {
let parsedJson = JSON.parse(input);
let ret = []
Object.keys(parsedJson).forEach((key)=>{
if(!isNaN(key)){
ret.push(parsedJson[key])
}else{
ret.push({key: parsedJson[key]})
}
})
return JSON.stringify(ret, null, 2)
} catch (error) {
return input
}
},

getPayloadData(original,current){
let changedKeys = []
let insertedKeys = []
Expand All @@ -81,7 +98,15 @@ const transform = {
for(const key in ogFlat){
let mainKey = '"' + key.split(".")?.pop() + '": '
if(!currFlat?.hasOwnProperty(key)){
deletedKeys.push({header: mainKey + '"' + ogFlat[key] + '"', className: 'deleted-content'})
let searchKey = "";
if(typeof(ogFlat[key]) === "string"){
searchKey = mainKey + '"' + ogFlat[key] + '"'
} else if(typeof(ogFlat[key]) === 'object'){
searchKey = mainKey
} else{
searchKey = mainKey + ogFlat[key]
}
deletedKeys.push({header: searchKey, className: 'deleted-content'})
finalUnflatObj[key] = ogFlat[key]
}else if(!func.deepComparison(ogFlat[key],currFlat[key])){
let searchKey = typeof(ogFlat[key]) === "string" ? mainKey + '"' + currFlat[key] + '"' : mainKey + currFlat[key]
Expand All @@ -105,14 +130,44 @@ const transform = {
return result;
}, {});

return{
json: func.unflattenObject(finalUnflatObj),
headersMap: mergedObject,
}
let ret = {};
ret.headersMap = mergedObject
ret.json = "";

let ogArr = typeof(original)==='string' ? original.split("\n") : []
let curArr = typeof(current)==='string' ? current.split("\n") : []

let retArr = []
for(let i in Array.from(Array(Math.max(ogArr.length, curArr.length)))){
if(ogArr[i] && curArr[i]){
if(ogArr[i]!==curArr[i]){
ret.headersMap[curArr[i]] = {className: 'updated-content', data: ogArr[i].replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') + "->" + curArr[i].replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'), keyLength: -2}
}
retArr.push(curArr[i]);
} else if(ogArr[i]) {
ret.headersMap[curArr[i]] = {className:'deleted-content'};
retArr.push(ogArr[i]);
} else if(curArr[i]){
ret.headersMap[curArr[i]] = {className:'added-content'};
retArr.push(curArr[i]);
}
}

if(typeof(current)!=='string' && typeof(original)!=='string'){
ret.json = this.formatJson(func.unflattenObject(finalUnflatObj));
} else if(typeof(current)==='string' && typeof(original)==='string'){
ret.json = retArr.join("\n")
} else if(typeof(current)==='string'){
ret.json = retArr.join("\n") + "\n" + this.formatJson(original);
} else {
ret.json = this.formatJson(current) + "\n" + retArr.join("\n");
}

return ret;
},

mergeDataObjs(lineObj, jsonObj, payloadObj){
let finalMessage = (lineObj.firstLine ? lineObj.firstLine: "") + "\n" + (jsonObj.message ? jsonObj.message: "") + "\n" + this.formatJson(payloadObj.json)
let finalMessage = (lineObj.firstLine ? lineObj.firstLine: "") + "\n" + (jsonObj.message ? jsonObj.message: "") + "\n" + this.processArrayJson(payloadObj.json)
return{
message: finalMessage,
firstLine: lineObj.original + "->" + lineObj.firstLine,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import transform from "../transform";
import func from "@/util/func";
import { ClockMinor,DynamicSourceMinor,LinkMinor } from '@shopify/polaris-icons';
import PersistStore from "../../../../main/PersistStore";
import { Button } from "@shopify/polaris";

const headers = [
{
Expand Down Expand Up @@ -136,6 +137,7 @@ function IssuesPage(){
const subCategoryMap = PersistStore(state => state.subCategoryMap);
const subCategoryFromSourceConfigMap = PersistStore(state => state.subCategoryFromSourceConfigMap);
const [issueStatus, setIssueStatus] = useState([]);
const [issuesFilters, setIssuesFilters] = useState({})
const [key, setKey] = useState(false);
const apiCollectionMap = PersistStore(state => state.collectionsMap);

Expand Down Expand Up @@ -253,7 +255,14 @@ function IssuesPage(){
let filterStatus = filters.issueStatus
setIssueStatus(filterStatus);
let startTimestamp = filters?.startTimestamp?.[0] || 0;

let obj = {
'filterStatus': filterStatus,
'filterCollectionsId': filterCollectionsId,
'filterSeverity': filterSeverity,
filterSubCategory: filterSubCategory,
startEpoch: startTimestamp
}
setIssuesFilters(obj)
await api.fetchIssues(skip, limit,filterStatus,filterCollectionsId,filterSeverity,filterSubCategory,startTimestamp).then((res) => {
total = res.totalIssuesCount;
ret = transform.prepareIssues(res, subCategoryMap, subCategoryFromSourceConfigMap, apiCollectionMap);
Expand All @@ -263,6 +272,11 @@ function IssuesPage(){
return {value:ret , total:total};
}

const openVulnerabilityReport = () => {
let summaryId = btoa(JSON.stringify(issuesFilters))
window.open('/dashboard/issues/summary/' + summaryId, '_blank');
}

return (
<PageWithMultipleCards
title="Issues"
Expand All @@ -286,7 +300,8 @@ function IssuesPage(){
getStatus={func.getTestResultStatus}
/>
]}
/>
primaryAction={<Button monochrome removeUnderline plain onClick={() => openVulnerabilityReport()}>Export vulnerability report</Button>}
/>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export default {
data: {skip, limit, filterStatus, filterCollectionsId, filterSeverity, filterSubCategory, startEpoch}
})
},
fetchVulnerableTestingRunResultsFromIssues(filters, skip) {
filters['skip'] = skip
return request({
url: 'api/fetchVulnerableTestingRunResultsFromIssues',
method: 'post',
data: filters
})
},
bulkUpdateIssueStatus (issueIdArray, statusToBeUpdated, ignoreReason) {
return request({
url: 'api/bulkUpdateIssueStatus',
Expand Down
Loading

0 comments on commit 0ca1964

Please sign in to comment.