mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-12 14:34:16 +00:00
+23









Josema Camacho
César Arroba
Alan Buscaglia
Claude
Andoni Alonso
Rubén De la Torre Vico
HugoPBrito
Hugo Pereira Brito
Pepe Fagoaga
Copilot Autofix powered by AI
Chandrapal Badshah
Chandrapal Badshah
Adrián Peña
Pedro Martín
KonstGolfi
lydiavilchez
Prowler Bot
prowler-bot
StylusFrost
dependabot[bot]
alejandrobailo
Alejandro Bailo
Víctor Fernández Poyatos
bota4go
Daniel Barranquero
Daniel Barranquero
mchennai
Ryan Nolette
Ulissis Correa
Sergio Garcia
Lee Trout
Sergio Garcia
Alan-TheGentleman
032499c29a
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: César Arroba <19954079+cesararroba@users.noreply.github.com> Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Andoni Alonso <14891798+andoniaf@users.noreply.github.com> Co-authored-by: Rubén De la Torre Vico <ruben@prowler.com> Co-authored-by: HugoPBrito <hugopbrit@gmail.com> Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Co-authored-by: Pepe Fagoaga <pepe@prowler.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Chandrapal Badshah <Chan9390@users.noreply.github.com> Co-authored-by: Chandrapal Badshah <12944530+Chan9390@users.noreply.github.com> Co-authored-by: Adrián Peña <adrianjpr@gmail.com> Co-authored-by: Pedro Martín <pedromarting3@gmail.com> Co-authored-by: KonstGolfi <73020281+KonstGolfi@users.noreply.github.com> Co-authored-by: lydiavilchez <114735608+lydiavilchez@users.noreply.github.com> Co-authored-by: Prowler Bot <bot@prowler.com> Co-authored-by: prowler-bot <179230569+prowler-bot@users.noreply.github.com> Co-authored-by: StylusFrost <43682773+StylusFrost@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: alejandrobailo <alejandrobailo94@gmail.com> Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Co-authored-by: Víctor Fernández Poyatos <victor@prowler.com> Co-authored-by: bota4go <108249054+bota4go@users.noreply.github.com> Co-authored-by: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com> Co-authored-by: mchennai <50082780+mchennai@users.noreply.github.com> Co-authored-by: Ryan Nolette <sonofagl1tch@users.noreply.github.com> Co-authored-by: Ulissis Correa <123517149+ulissisc@users.noreply.github.com> Co-authored-by: Sergio Garcia <hello@mistercloudsec.com> Co-authored-by: Lee Trout <ltrout@watchpointlabs.com> Co-authored-by: Sergio Garcia <sergargar1@gmail.com> Co-authored-by: Alan-TheGentleman <alan@thegentleman.dev>
165 lines
5.0 KiB
TypeScript
165 lines
5.0 KiB
TypeScript
import {
|
|
AttackPathGraphData,
|
|
GraphEdge,
|
|
GraphNodeProperties,
|
|
GraphNodePropertyValue,
|
|
GraphRelationship,
|
|
} from "@/types/attack-paths";
|
|
|
|
/**
|
|
* Normalizes property values to ensure they are primitives
|
|
* Arrays are converted to comma-separated strings
|
|
*
|
|
* @param value - The property value to normalize
|
|
* @returns Normalized primitive value
|
|
*/
|
|
function normalizePropertyValue(
|
|
value:
|
|
| GraphNodePropertyValue
|
|
| GraphNodePropertyValue[]
|
|
| Record<string, unknown>,
|
|
): string | number | boolean | null | undefined {
|
|
if (value === null || value === undefined) {
|
|
return value;
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
// Convert arrays to comma-separated strings
|
|
return value.join(", ");
|
|
}
|
|
|
|
if (
|
|
typeof value === "string" ||
|
|
typeof value === "number" ||
|
|
typeof value === "boolean"
|
|
) {
|
|
return value;
|
|
}
|
|
|
|
// For any other type, convert to string
|
|
return String(value);
|
|
}
|
|
|
|
/**
|
|
* Normalizes all properties in an object to ensure they are primitives
|
|
*
|
|
* @param properties - The properties object to normalize
|
|
* @returns Normalized properties object
|
|
*/
|
|
function normalizeProperties(
|
|
properties: Record<
|
|
string,
|
|
GraphNodePropertyValue | GraphNodePropertyValue[] | Record<string, unknown>
|
|
>,
|
|
): GraphNodeProperties {
|
|
const normalized: GraphNodeProperties = {};
|
|
|
|
for (const [key, value] of Object.entries(properties)) {
|
|
normalized[key] = normalizePropertyValue(value);
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
/**
|
|
* Adapts graph query result data for D3 visualization
|
|
* Transforms relationships array into edges array for D3 force-directed graph
|
|
*
|
|
* The adapter handles:
|
|
* - Converting relationship objects to edge objects compatible with D3
|
|
* - Mapping relationship labels to edge types for graph styling
|
|
* - Normalizing array properties to strings (e.g., anonymous_actions: ["s3:GetObject"] -> "s3:GetObject")
|
|
* - Preserving node and relationship data structure
|
|
* - Adding findings array to each node based on HAS_FINDING edges
|
|
* - Adding resources array to finding nodes based on HAS_FINDING edges (reverse relationship)
|
|
*
|
|
* @param graphData - Raw graph data with nodes and relationships from API
|
|
* @returns Graph data with edges array formatted for D3 visualization and findings/resources on nodes
|
|
*/
|
|
export function adaptQueryResultToGraphData(
|
|
graphData: AttackPathGraphData,
|
|
): AttackPathGraphData {
|
|
// Normalize node properties to ensure all values are primitives
|
|
const normalizedNodes = graphData.nodes.map((node) => ({
|
|
...node,
|
|
properties: normalizeProperties(
|
|
node.properties as Record<
|
|
string,
|
|
GraphNodePropertyValue | GraphNodePropertyValue[]
|
|
>,
|
|
),
|
|
findings: [] as string[], // Will be populated below
|
|
resources: [] as string[], // Will be populated below for finding nodes
|
|
}));
|
|
|
|
// Transform relationships into D3-compatible edges if relationships exist
|
|
// Also handle case where edges are already provided (e.g., from mock data)
|
|
let edges: GraphEdge[] = [];
|
|
|
|
if (graphData.relationships) {
|
|
edges = (graphData.relationships as GraphRelationship[]).map(
|
|
(relationship) => ({
|
|
id: relationship.id,
|
|
source: relationship.source,
|
|
target: relationship.target,
|
|
type: relationship.label, // D3 uses 'type' for styling edge appearance
|
|
properties: relationship.properties
|
|
? normalizeProperties(
|
|
relationship.properties as Record<
|
|
string,
|
|
GraphNodePropertyValue | GraphNodePropertyValue[]
|
|
>,
|
|
)
|
|
: undefined,
|
|
}),
|
|
);
|
|
} else if (graphData.edges) {
|
|
// If edges are already provided, just normalize their properties
|
|
edges = (graphData.edges as GraphEdge[]).map((edge) => ({
|
|
...edge,
|
|
properties: edge.properties
|
|
? normalizeProperties(
|
|
edge.properties as Record<
|
|
string,
|
|
GraphNodePropertyValue | GraphNodePropertyValue[]
|
|
>,
|
|
)
|
|
: undefined,
|
|
}));
|
|
}
|
|
|
|
// Populate findings and resources based on HAS_FINDING edges
|
|
edges.forEach((edge) => {
|
|
if (edge.type === "HAS_FINDING") {
|
|
const sourceId =
|
|
typeof edge.source === "string"
|
|
? edge.source
|
|
: (edge.source as { id?: string })?.id;
|
|
const targetId =
|
|
typeof edge.target === "string"
|
|
? edge.target
|
|
: (edge.target as { id?: string })?.id;
|
|
|
|
if (sourceId && targetId) {
|
|
// Add finding to source node (resource -> finding)
|
|
const sourceNode = normalizedNodes.find((n) => n.id === sourceId);
|
|
if (sourceNode) {
|
|
sourceNode.findings.push(targetId);
|
|
}
|
|
|
|
// Add resource to target node (finding <- resource)
|
|
const targetNode = normalizedNodes.find((n) => n.id === targetId);
|
|
if (targetNode) {
|
|
targetNode.resources.push(sourceId);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return {
|
|
nodes: normalizedNodes,
|
|
edges,
|
|
relationships: graphData.relationships, // Preserve original relationships data
|
|
};
|
|
}
|