#!/bin/bash set -e # Run from the deploy/ directory cd "$(dirname "$0")" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color echo -e "${BLUE}" cat << "EOF" ______ __ __ ___ / ____// /_ ____ _ _____ __ __ / /_ ____/ (_)____ / / / __ \ / __ `// ___// / / // __ \ / __ // // __ \ / /___ / / / // /_/ // / / /_/ // /_/ // /_/ // // /_/ / \____//_/ /_/ \__,_//_/ \__, //_.___/ \__,_//_/ \____/ /____/ EOF echo -e "${NC}" echo -e "${GREEN}Charybdis Demo${NC}\n" # Detect gRPC client GRPC_CMD="" if command -v buf &> /dev/null; then GRPC_CMD="buf" elif command -v grpcurl &> /dev/null; then GRPC_CMD="grpcurl" else echo -e "${RED}Error: Neither 'buf' nor 'grpcurl' found. Install one:${NC}" echo -e " brew install bufbuild/buf/buf" echo -e " brew install grpcurl" exit 1 fi echo -e "${BLUE}Using gRPC client: ${GRPC_CMD}${NC}\n" # Helper function for gRPC calls grpc_call() { local service_method="$1" local data="$2" local host="${3:-localhost:50051}" if [ "$GRPC_CMD" = "buf" ]; then buf curl --protocol grpc --http2-prior-knowledge \ -d "$data" \ "http://${host}/${service_method}" 2>&1 else grpcurl -plaintext -d "$data" "$host" "$service_method" 2>&1 fi } # Check if Docker is running if ! docker info > /dev/null 2>&1; then echo -e "${RED}Error: Docker is not running. Please start Docker first.${NC}" exit 1 fi echo -e "${BLUE}Starting services...${NC}\n" # Start core services (Charybdis + Postgres only for quick demo) docker compose -f docker-compose.demo.yml up -d postgres-charybdis 2>/dev/null echo -e "${GREEN} Postgres started${NC}" # Wait for Postgres for i in {1..15}; do if docker compose -f docker-compose.demo.yml exec -T postgres-charybdis pg_isready -U charybdis > /dev/null 2>&1; then break fi sleep 1 done docker compose -f docker-compose.demo.yml up -d charybdis 2>/dev/null echo -e "${GREEN} Charybdis started${NC}" # Wait for Charybdis gRPC echo -e "\n${BLUE}Waiting for Charybdis to be ready...${NC}" for i in {1..30}; do if timeout 2 bash -c "echo > /dev/tcp/localhost/50051" 2>/dev/null; then echo -e "${GREEN} gRPC server ready on :50051${NC}" break fi if [ $i -eq 30 ]; then echo -e "${RED} Charybdis failed to start${NC}" docker compose -f docker-compose.demo.yml logs charybdis | tail -10 exit 1 fi sleep 1 done sleep 2 # Allow service to fully initialize # --- Demo: Entity Management --- echo -e "\n${BLUE}=== Entity Management ===${NC}\n" echo -e "${YELLOW}1. Creating a Component...${NC}" RESULT=$(grpc_call "charybdis.entities.EntityService/CreateEntity" '{ "entity": { "kind": "Component", "component_metadata": { "name": "payment-service", "namespace": "default", "description": "Payment processing microservice" }, "component_spec": { "type": "service", "lifecycle": "production", "owner": "team-payments" } } }') echo "$RESULT" | head -5 ENTITY_ID=$(echo "$RESULT" | grep '"id"' | head -1 | sed 's/.*"id": *"//;s/".*//') echo -e "${GREEN} Created entity: ${ENTITY_ID}${NC}\n" echo -e "${YELLOW}2. Listing entities...${NC}" grpc_call "charybdis.entities.EntityService/ListEntities" '{}' | head -5 echo -e "" # --- Demo: Vulnerability Ingestion --- echo -e "\n${BLUE}=== Vulnerability Ingestion ===${NC}\n" # Create a SARIF report SARIF_REPORT=$(cat << 'SARIF' { "version": "2.1.0", "runs": [{ "tool": {"driver": {"name": "semgrep", "version": "1.0.0", "rules": [ {"id": "sql-injection", "shortDescription": {"text": "SQL Injection"}, "defaultConfiguration": {"level": "error"}, "properties": {"tags": ["CWE-89"]}}, {"id": "xss-reflected", "shortDescription": {"text": "Reflected XSS"}, "defaultConfiguration": {"level": "warning"}, "properties": {"tags": ["CWE-79"]}}, {"id": "hardcoded-secret", "shortDescription": {"text": "Hardcoded Secret"}, "defaultConfiguration": {"level": "error"}} ]}}, "results": [ {"ruleId": "sql-injection", "level": "error", "message": {"text": "User input in SQL query without parameterization"}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/db/queries.rs"}, "region": {"startLine": 42}}}], "partialFingerprints": {"primaryLocationLineHash": "fp-sql-001"}}, {"ruleId": "xss-reflected", "level": "warning", "message": {"text": "User input reflected in response without encoding"}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/handlers/search.rs"}, "region": {"startLine": 18}}}]}, {"ruleId": "hardcoded-secret", "level": "error", "message": {"text": "AWS secret key found in source code"}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/config.rs"}, "region": {"startLine": 7}}}]} ] }] } SARIF ) SARIF_B64=$(echo "$SARIF_REPORT" | base64) echo -e "${YELLOW}3. Dry-run scan (preview without persisting)...${NC}" grpc_call "charybdis.ingestion.IngestionService/DryRunScan" "{ \"component_ref\": \"payment-service\", \"lifecycle\": \"production\", \"format\": \"sarif\", \"data\": \"${SARIF_B64}\" }" echo -e "" echo -e "${YELLOW}4. Importing scan (persisting findings)...${NC}" grpc_call "charybdis.ingestion.IngestionService/ImportScan" "{ \"component_ref\": \"payment-service\", \"lifecycle\": \"production\", \"format\": \"sarif\", \"data\": \"${SARIF_B64}\" }" echo -e "" echo -e "${YELLOW}5. Re-importing same scan (testing deduplication)...${NC}" grpc_call "charybdis.ingestion.IngestionService/ImportScan" "{ \"component_ref\": \"payment-service\", \"lifecycle\": \"production\", \"format\": \"sarif\", \"data\": \"${SARIF_B64}\" }" echo -e "" # Simulate a fix: remove the SQL injection finding SARIF_FIXED=$(cat << 'SARIF' { "version": "2.1.0", "runs": [{ "tool": {"driver": {"name": "semgrep", "version": "1.0.0", "rules": [ {"id": "xss-reflected", "shortDescription": {"text": "Reflected XSS"}, "defaultConfiguration": {"level": "warning"}, "properties": {"tags": ["CWE-79"]}}, {"id": "hardcoded-secret", "shortDescription": {"text": "Hardcoded Secret"}, "defaultConfiguration": {"level": "error"}} ]}}, "results": [ {"ruleId": "xss-reflected", "level": "warning", "message": {"text": "User input reflected in response without encoding"}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/handlers/search.rs"}, "region": {"startLine": 18}}}]}, {"ruleId": "hardcoded-secret", "level": "error", "message": {"text": "AWS secret key found in source code"}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/config.rs"}, "region": {"startLine": 7}}}]} ] }] } SARIF ) SARIF_FIXED_B64=$(echo "$SARIF_FIXED" | base64) echo -e "${YELLOW}6. Importing scan after fix (SQL injection resolved)...${NC}" grpc_call "charybdis.ingestion.IngestionService/ImportScan" "{ \"component_ref\": \"payment-service\", \"lifecycle\": \"production\", \"format\": \"sarif\", \"data\": \"${SARIF_FIXED_B64}\" }" echo -e "" # --- Summary --- echo -e "\n${GREEN}=== Demo Complete ===${NC}\n" echo -e "${BLUE}What we demonstrated:${NC}" echo -e " 1. Created a Component entity via gRPC" echo -e " 2. Dry-run scan: preview findings without persisting" echo -e " 3. Import scan: persist 3 findings (SQL injection, XSS, hardcoded secret)" echo -e " 4. Deduplication: re-import same scan -> all unchanged" echo -e " 5. Auto-resolution: import without SQL injection -> marked as resolved" echo "" echo -e "${BLUE}Access:${NC}" echo -e " gRPC: localhost:50051" echo -e " YAML Adapter: http://localhost:8081" echo "" echo -e "${BLUE}Stop:${NC}" echo -e " docker compose -f docker-compose.demo.yml down" echo "" echo -e "${BLUE}Clean up:${NC}" echo -e " docker compose -f docker-compose.demo.yml down -v" echo ""