Skip to content

Commit

Permalink
Merge pull request #2098 from akto-api-security/feature/source_code_t…
Browse files Browse the repository at this point in the history
…esting

testing using source code
  • Loading branch information
notshivansh authored Feb 18, 2025
2 parents 2cf6a74 + e29de19 commit 7b2b4db
Show file tree
Hide file tree
Showing 18 changed files with 501 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -305,7 +306,9 @@ public String syncExtractedAPIs() {
singleTypeInfos.addAll(generateSTIsFromPayload(apiCollection.getId(), codeAnalysisApi.getEndpoint(), codeAnalysisApi.getMethod(), requestBody, -1));
singleTypeInfos.addAll(generateSTIsFromPayload(apiCollection.getId(), codeAnalysisApi.getEndpoint(), codeAnalysisApi.getMethod(), responseBody, 200));

Bson update = Updates.combine(Updates.max(SingleTypeInfo.LAST_SEEN, now), Updates.setOnInsert("timestamp", now));
Bson update = Updates.combine(Updates.max(SingleTypeInfo.LAST_SEEN, now),
Updates.setOnInsert("timestamp", now),
Updates.set(SingleTypeInfo._COLLECTION_IDS, Arrays.asList(apiCollection.getId())));

for (SingleTypeInfo singleTypeInfo: singleTypeInfos) {
bulkUpdatesSTI.add(
Expand Down
65 changes: 48 additions & 17 deletions apps/dashboard/src/main/java/com/akto/action/DependencyAction.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
package com.akto.action;

import com.akto.DaoInit;
import com.akto.dao.*;
import com.akto.dao.context.Context;
import com.akto.dto.ApiCollection;
import com.akto.dto.ApiInfo;
import com.akto.dto.OriginalHttpRequest;
import com.akto.dto.OriginalHttpResponse;
import com.akto.dto.dependency_flow.*;
import com.akto.dto.traffic.SampleData;
import com.akto.dto.type.APICatalog;
import com.akto.dto.type.URLMethods;
import com.akto.dto.type.URLMethods.Method;
import com.akto.log.LoggerMaker;
import com.akto.log.LoggerMaker.LogDb;
import com.akto.runtime.RelationshipSync;
import com.akto.test_editor.execution.Build;
import com.akto.utils.Utils;
import com.mongodb.BasicDBObject;
import com.mongodb.ConnectionString;
import com.mongodb.client.model.*;
import org.apache.logging.log4j.util.Strings;
import org.bson.conversions.Bson;

import java.util.*;
Expand All @@ -35,7 +30,7 @@ public class DependencyAction extends UserAction {

private Collection<Node> result;

private static final LoggerMaker loggerMaker = new LoggerMaker(DependencyAction.class);
private static final LoggerMaker loggerMaker = new LoggerMaker(DependencyAction.class,LogDb.DASHBOARD);
private boolean dependencyGraphExists = false;
public String checkIfDependencyGraphAvailable() {

Expand Down Expand Up @@ -69,7 +64,6 @@ public String execute() {
private int total;
private int skip;


public String buildDependencyTable() {
List<Node> nodes = DependencyFlowNodesDao.instance.findNodesForCollectionIds(apiCollectionIds,false, skip, 50);
dependencyTableList = new ArrayList<>();
Expand All @@ -79,6 +73,12 @@ public String buildDependencyTable() {
apiInfoKeys.add(new ApiInfo.ApiInfoKey(Integer.parseInt(node.getApiCollectionId()), node.getUrl(), URLMethods.Method.fromString(node.getMethod())));
}
Map<ApiInfo.ApiInfoKey, List<String>> parametersMap = SingleTypeInfoDao.instance.fetchRequestParameters(apiInfoKeys);
Map<ApiInfo.ApiInfoKey, List<String>> sourceCodeParametersMap = CodeAnalysisSingleTypeInfoDao.instance.fetchRequestParameters(apiInfoKeys);

// Add parameters from source code, if any.
if (sourceCodeParametersMap != null && !sourceCodeParametersMap.isEmpty()) {
parametersMap.putAll(sourceCodeParametersMap);
}

replaceDetails = ReplaceDetailsDao.instance.findAll(Filters.in(ReplaceDetail._API_COLLECTION_ID, apiCollectionIds));

Expand All @@ -97,17 +97,36 @@ public String buildDependencyTable() {
private int newCollectionId;

private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

private boolean sourceCodeApis;

public String invokeDependencyTable() {
ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction();
apiCollectionsAction.setCollectionName("temp " + Context.now());
apiCollectionsAction.createCollection();
List<ApiCollection> apiCollections = apiCollectionsAction.getApiCollections();;

if (apiCollections == null || apiCollections.size() == 0) {
addActionError("Couldn't create collection");
if(apiCollectionIds== null || apiCollectionIds.isEmpty()){
addActionError("No API collections to invoke dependency graph");
return ERROR.toUpperCase();
}
newCollectionId = apiCollections.get(0).getId();

if (sourceCodeApis && apiCollectionIds.size() > 1) {
addActionError("Please use a single API collection ID with source code APIs");
return ERROR.toUpperCase();
}

if(!sourceCodeApis){
ApiCollectionsAction apiCollectionsAction = new ApiCollectionsAction();
apiCollectionsAction.setCollectionName("temp " + Context.now());
apiCollectionsAction.createCollection();
List<ApiCollection> apiCollections = apiCollectionsAction.getApiCollections();;

if (apiCollections == null || apiCollections.size() == 0) {
addActionError("Couldn't create collection");
return ERROR.toUpperCase();
}
newCollectionId = apiCollections.get(0).getId();
} else {
// Insert in original collection for source code APIs.
newCollectionId = apiCollectionIds.get(0);
}

int accountId = Context.accountId.get();

Expand All @@ -121,14 +140,19 @@ public void run() {
for (ReplaceDetail replaceDetail: replaceDetailsFromDb) {
replaceDetailMap.put(replaceDetail.hashCode(), replaceDetail);
}
List<Build.RunResult> runResults = build.run(apiCollectionIds, modifyHostDetails, replaceDetailMap);
List<Build.RunResult> runResults = build.run(apiCollectionIds, modifyHostDetails, replaceDetailMap, sourceCodeApis);
List<String> messages = new ArrayList<>();

for (Build.RunResult runResult: runResults) {
String currentMessage = runResult.getCurrentMessage();
messages.add(currentMessage);
}

if(messages.isEmpty()){
loggerMaker.infoAndAddToDb("No messages found for invokeDependencyTable");
return;
}

try {
Utils.pushDataToKafka(newCollectionId, "", messages, new ArrayList<>(), true, true);
} catch (Exception e) {
Expand Down Expand Up @@ -313,5 +337,12 @@ public boolean getDependencyGraphExists() {
return dependencyGraphExists;
}


public boolean getSourceCodeApis() {
return sourceCodeApis;
}

public void setSourceCodeApis(boolean sourceCodeApis) {
this.sourceCodeApis = sourceCodeApis;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Button, HorizontalStack, Icon, Link, Modal, Select, Spinner, Text, TextField, VerticalStack } from "@shopify/polaris";
import { useEffect, useRef, useState } from "react";
import { Button, HorizontalStack, Icon, Link, Popover, Spinner, Text } from "@shopify/polaris";
import { useState } from "react";
import PageWithMultipleCards from "../../../components/layouts/PageWithMultipleCards";
import GithubServerTable from "../../../components/tables/GithubServerTable";
import { CellType } from "../../../components/tables/rows/GithubRow";
Expand Down Expand Up @@ -58,6 +58,7 @@ function DependencyTable() {
const [runResults, setRunResults] = useState({})
const [refresh, setRefresh] = useState(false)
const [invokeLoading, setInvokeLoading] = useState(false)
const [invokeLoadingSecond, setInvokeLoadingSecond] = useState(false)

const [active, setActive] = useState(false);
const [editApiCollectionId, setEditApiCollectionId] = useState(null)
Expand Down Expand Up @@ -113,6 +114,10 @@ function DependencyTable() {
})
})

res = res.sort((a, b) => {
return a.childParam.localeCompare(b.childParam)
})

return res
}

Expand Down Expand Up @@ -194,6 +199,15 @@ function DependencyTable() {
setEditData(newEditData);
}

function isBoolean(value) {
return (
typeof value === "boolean" ||
(typeof value === "string" && (value.toLowerCase() === "true" || value.toLowerCase() === "false"))
);
}

const isInvalidNumber = (value) => value.trim() === "" || isNaN(value);

const convertDataToKVPairList = (data) => {
let kvPairs = []
data.forEach((x) => {
Expand All @@ -203,7 +217,7 @@ function DependencyTable() {
"isHeader": x["childParamIsHeader"],
"isUrlParam": x["childParamIsUrlParam"],
"value": x["value"],
"type": "STRING"
"type": isInvalidNumber(x["value"]) ? (isBoolean(x["value"]) ? "BOOLEAN" : "STRING") : "INTEGER"
})
})

Expand Down Expand Up @@ -253,21 +267,23 @@ function DependencyTable() {

const components = [resultTable, modalComponent, globalVarModalComponent]

const invokeDependencyTable = () => {
if (invokeLoading) return
setInvokeLoading(true)
api.invokeDependencyTable(apiCollectionIds).then((resp) => {
const invokeDependencyTable = (sourceCodeApis, updateFunc) => {
if (invokeLoading || invokeLoadingSecond) return
updateFunc(true)
api.invokeDependencyTable(apiCollectionIds, sourceCodeApis).then((resp) => {
let newCollectionId = resp["newCollectionId"]
// let temp = {}
// runResultList.forEach((runResult) => {
// let apiInfoKey = runResult["apiInfoKey"]
// temp[apiInfoKey["method"] + " " + apiInfoKey["url"]] = runResult
// })

setInvokeLoading(false)
updateFunc(false)
// setRunResults(temp)
// setRefresh(!refresh)

if(!sourceCodeApis){

const url = "/dashboard/observe/inventory/" + newCollectionId

const forwardLink = (
Expand All @@ -279,13 +295,37 @@ function DependencyTable() {
)

func.setToast(true, false, forwardLink)
}
})
}

const [moreActions, setMoreActions] = useState(false)

const secondaryActionsComponent = (
<Button onClick={invokeDependencyTable} primary >
{invokeLoading ? <Spinner size="small" /> : "Invoke"}
</Button>
<Popover
active={moreActions}
activator={(
<Button onClick={() => setMoreActions(!moreActions)} disclosure removeUnderline>
Invoke
</Button>
)}
autofocusTarget="first-node"
onClose={() => { setMoreActions(false) }}
preferredAlignment="right"
>
<Popover.Pane fixed>
<Popover.Section>
<Button plain monochrome onClick={() => invokeDependencyTable(false, setInvokeLoading)} removeUnderline>
{invokeLoading ? <Spinner size="small" /> : "Invoke"}
</Button>
</Popover.Section>
<Popover.Section>
<Button plain monochrome onClick={() => invokeDependencyTable(true, setInvokeLoadingSecond)} removeUnderline>
{invokeLoadingSecond ? <Spinner size="small" /> : "Invoke for source code APIs"}
</Button>
</Popover.Section>
</Popover.Pane>
</Popover>
)

const globalVarsComponent = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function EditModal(props) {
content: 'Save',
onAction: () => {saveEditData(editData)},
}}
large={true}
>
<Modal.Section>
<VerticalStack gap={2}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function EditTextField(ele, modifyEditData) {
onChange={(newVal) => { handleTextFieldChange(newVal) }}
autoComplete="off"
connectedLeft={
<Box width='160px'>
<Box width="500px">
<TooltipText tooltip={ele["childParam"]} text={ele["childParam"]} textProps={{ color: "subdued", variant: "bodyLg" }} />
</Box>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,12 +318,13 @@ export default {
data: {}
})
},
invokeDependencyTable(apiCollectionIds){
invokeDependencyTable(apiCollectionIds, sourceCodeApis){
return request({
url: '/api/invokeDependencyTable',
method: 'post',
data: {
apiCollectionIds
apiCollectionIds,
sourceCodeApis
}
})
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ function UserConfig() {
async function addOrUpdateScript() {
if (preRequestScript.id) {
api.updateScript(preRequestScript.id, preRequestScript.javascript)
func.setToast(true, false, "Pre-request script updated")
} else {
api.addScript(preRequestScript)
func.setToast(true, false, "Pre-request script added")
}
}

Expand Down
Loading

0 comments on commit 7b2b4db

Please sign in to comment.