From d8a8b3e733b4700d8a1c3b28303f386b1a017afd Mon Sep 17 00:00:00 2001 From: Tapas Thakkar Date: Tue, 16 Jun 2026 15:17:02 +0000 Subject: [PATCH] 524620481 Fix CVE-2026-53550 --- .gitignore | 3 + npm-shrinkwrap.json | 158 +++++------ package.json | 11 +- test-functional/DockerSanityTests.sh | 388 +++++++++++++++++++++++++++ 4 files changed, 458 insertions(+), 102 deletions(-) create mode 100644 test-functional/DockerSanityTests.sh diff --git a/.gitignore b/.gitignore index 8ec7512f..e79aeac2 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ test-e2e-local/test3.yaml test-e2e-local/leddyr-eval-test-config-BK.yaml test-e2e-local/tmp_emg_file.yaml test-stress/rateset.txt +bin +.agents +.lumbergh \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 5c6ede45..aeb33f11 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "edgemicro", - "version": "3.3.10", + "version": "3.3.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "edgemicro", - "version": "3.3.10", + "version": "3.3.11", "cpu": [ "!arm", "!mips" @@ -20,7 +20,7 @@ "debug": "^3.1.0", "diff": "^8.0.3", "fs-extra": "^11.2.0", - "js-yaml": "^4.1.0", + "js-yaml": "^4.2.0", "jsonwebtoken": "^9.0.1", "lodash": "^4.17.23", "microgateway-edgeauth": "^3.2.2", @@ -36,7 +36,7 @@ "sanitizer": "^0.1.3", "tmp": "^0.2.5", "tunnel-agent": "^0.6.0", - "uuid": "^14.0.0", + "uuid": "^11.1.1", "volos-util-apigee": "^0.1.7", "xml2js": "^0.5.0" }, @@ -571,9 +571,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -766,16 +766,6 @@ "node": ">=8" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -790,20 +780,6 @@ "node": ">=8" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1074,9 +1050,9 @@ "license": "ISC" }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", "bin": { @@ -1266,9 +1242,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", - "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "version": "2.10.36", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.36.tgz", + "integrity": "sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1546,9 +1522,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "dev": true, "funding": [ { @@ -2063,9 +2039,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.362", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.362.tgz", - "integrity": "sha512-PUY2DrLvkjkUuWqq+KPL2iWshrJsZOcIojzRQ7eXFacc9dWga7MGMJAa15VbiejSZB1PAXaRLAiKgruHP8LB1w==", + "version": "1.5.372", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", + "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", "dev": true, "license": "ISC" }, @@ -2154,9 +2130,9 @@ } }, "node_modules/eslint": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", - "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", + "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", "dependencies": { @@ -2165,7 +2141,7 @@ "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2290,20 +2266,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -2825,9 +2787,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3191,9 +3153,9 @@ } }, "node_modules/istanbul-lib-processinfo": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-3.0.0.tgz", - "integrity": "sha512-P7nLXRRlo7Sqinty6lNa7+4o9jBUYGpqtejqCOZKfgXlRoxY/QArflcB86YO500Ahj4pDJEG34JjMRbQgePLnQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-3.0.1.tgz", + "integrity": "sha512-s3mX05h5wGZeScG6XnOanygPh4SJu5ujMc9YbvpnLGXWy1cRiGbp0NdVcjHxgoZt3WfQppfBsa0y+gWdYJ2pGQ==", "dev": true, "license": "ISC", "dependencies": { @@ -3201,8 +3163,7 @@ "cross-spawn": "^7.0.3", "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", - "rimraf": "^6.1.3", - "uuid": "^8.3.2" + "rimraf": "^6.1.3" }, "engines": { "node": "20 || >=22" @@ -3322,9 +3283,19 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4075,9 +4046,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", - "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", "dev": true, "license": "MIT", "engines": { @@ -5434,9 +5405,9 @@ } }, "node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5556,14 +5527,14 @@ "license": "MIT" }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -5794,13 +5765,6 @@ "dev": true, "license": "ISC" }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", @@ -6109,9 +6073,9 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.6.tgz", - "integrity": "sha512-5sJPdPjfI5Kx+qbrDesxkglRBxW//g7hCsqspEjwkewGvBMGIKMOTKzLt1hFVJzyadba3lDUN20O9qhvbQUSTA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "license": "MIT", "engines": { "node": ">=14.14" @@ -6300,16 +6264,16 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", - "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist-node/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/verror": { diff --git a/package.json b/package.json index 9ab114da..737b5a60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "edgemicro", - "version": "3.3.10", + "version": "3.3.11", "description": "Apigee Edge Microgateway", "main": "index.js", "license": "Apache-2.0", @@ -15,7 +15,7 @@ "debug": "^3.1.0", "diff": "^8.0.3", "fs-extra": "^11.2.0", - "js-yaml": "^4.1.0", + "js-yaml": "^4.2.0", "jsonwebtoken": "^9.0.1", "lodash": "^4.17.23", "microgateway-edgeauth": "^3.2.2", @@ -31,21 +31,22 @@ "sanitizer": "^0.1.3", "tmp": "^0.2.5", "tunnel-agent": "^0.6.0", - "uuid": "^14.0.0", + "uuid": "^11.1.1", "volos-util-apigee": "^0.1.7", "xml2js": "^0.5.0" }, "overrides": { + "js-yaml": "^4.2.0", "diff": "^8.0.3", "serialize-javascript": "^7.0.4", "brace-expansion": "^5.0.5", "glob": "^10.4.5", "postman-request": { - "uuid": "^14.0.0", + "uuid": "^11.1.1", "qs": "^6.15.1" }, "istanbul-lib-processinfo": { - "uuid": "^14.0.0" + "uuid": "^11.1.1" } }, "devDependencies": { diff --git a/test-functional/DockerSanityTests.sh b/test-functional/DockerSanityTests.sh new file mode 100644 index 00000000..e7d55050 --- /dev/null +++ b/test-functional/DockerSanityTests.sh @@ -0,0 +1,388 @@ +#!/bin/bash + +# Check if docker image is provided +if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Example: $0 gcr.io/apigee-microgateway/edgemicro:3.3.10" + exit 1 +fi + +DOCKER_IMAGE="$1" + +# Import helpers +source ./testhelper.sh +source ./testEMG.sh + +# Configuration +proxyNamePrefix="edgemicro_" +proxyTargetUrl="http://mocktarget.apigee.net/json" + +EMG_CONFIG_DIR="$HOME/.edgemicro" +EMG_CONFIG_FILE="$HOME/.edgemicro/$MOCHA_ORG-$MOCHA_ENV-config.yaml" + +PRODUCT_NAME="edgemicro_product_docker_sanity" +PROXY_NAME="edgemicro_proxy_docker_sanity" +PROXY_NAME_QUOTA="edgemicro_proxy_docker_sanity" +DEVELOPER_NAME="edgemicro_dev_docker_sanity_$$" +DEVELOPER_APP_NAME="edgemicro_dev_app_docker_sanity_$$" + +TIMESTAMP=`date "+%Y-%m-%d-%H"` +LOGFILE="DockerSanityTestLog.$TIMESTAMP" + +RED=`tput setaf 1` +GREEN=`tput setaf 2` +NC=`tput sgr0` + +STATUS_PASS_STR="Status: ${GREEN}PASS${NC}" +STATUS_FAIL_STR="Status: ${RED}FAIL${NC}" + +# Set the EDGEMICRO CLI tool path +EDGEMICRO="node ../cli/edgemicro" +LOCAL_REPOSITORY_TESTING=$(pwd) + +initEMG() { + local result=0 + logInfo "Initialize EMG (Custom)" + mkdir -p "$EMG_CONFIG_DIR" + $EDGEMICRO init > initEMG.txt 2>&1 + result=$? + if [ $result -eq 0 ]; then + logInfo "Initialize EMG with status $result" + else + logError "Failed to initialize EMG. Output:" + cat initEMG.txt >> "$LOGFILE" + cat initEMG.txt >&2 + fi + rm -f initEMG.txt + return $result +} + +configureEMG() { + local result=0 + logInfo "Configure EMG (Custom)" + $EDGEMICRO configure -o $MOCHA_ORG -e $MOCHA_ENV -u $MOCHA_USER -t $MOCHA_BEARER_TOKEN > edgemicro.configure.txt 2>&1 + result=$? + if [ $result -eq 0 ]; then + if [ ! -f $EMG_CONFIG_FILE ]; then + result=1 + logError "Failed to configure EMG and creation of $EMG_CONFIG_FILE" + else + logInfo "Successfully configured EMG with status $result" + fi + else + logError "Failed to configure EMG. Output:" + cat edgemicro.configure.txt >> "$LOGFILE" + cat edgemicro.configure.txt >&2 + fi + return $result +} + +testApiProxyWithAuthToken() { + local result=0 + local ret=0 + + logInfo "Test Auth Token (Overridden)" + apiKeysJson=$(getDeveloperApiKey "${DEVELOPER_NAME}" "${DEVELOPER_APP_NAME}") + consumerKey=$(echo "$apiKeysJson" | jq -r '.consumerKey') + consumerSecret=$(echo "$apiKeysJson" | jq -r '.consumerSecret') + TOKEN=$(getAuthToken "$consumerKey" "$consumerSecret") + + curl -q -s http://localhost:8000/v1/${PROXY_NAME} -H "Authorization: Bearer $TOKEN" -D headers.txt -o proxy_response.txt ; ret=$? + result=$(grep HTTP headers.txt | cut -d ' ' -f2) + if [ ${ret} -eq 0 -a ${result} -eq 200 ]; then + logInfo "Successfully tested API Proxy using auth token with code $result" + else + logError "Failed to test API Proxy using auth token with code $result" + logError "--- API Error Response ---" >&2 + cat proxy_response.txt >&2 + logError "--------------------------" >&2 + ret=1 + fi + + rm -f headers.txt proxy_response.txt + + return $ret +} + +# Let's restore the actual startEMGDocker with parsing key/secret +startEMGDocker() { + local image=$1 + logInfo "Start EMG in Docker: $image" + + # We need the key and secret to run EMG. Since edgemicro configure prints them, we save them to edgemicro.configure.txt during configureEMG. + # Let's read them from edgemicro.configure.txt before deleting it. + # So in configureEMG, we shouldn't delete edgemicro.configure.txt immediately. + # Let's verify startEMGDocker reads from edgemicro.configure.txt. + EMG_KEY=$(cat edgemicro.configure.txt | grep "key:" | cut -d ' ' -f8) + EMG_SECRET=$(cat edgemicro.configure.txt | grep "secret:" | cut -d ' ' -f8) + if [ -z "$EMG_KEY" ] || [ -z "$EMG_SECRET" ]; then + logError "Failed to retrieve emg key and secret from edgemicro.configure.txt" + return 1 + fi + + if [ ! -f "$EMG_CONFIG_FILE" ]; then + logError "Failed to locate EMG config file $EMG_CONFIG_FILE" + return 1 + fi + + # Base64 encode the config + if [[ "$OSTYPE" == "darwin"* ]]; then + EMG_CONFIG_BASE64=$(cat "$EMG_CONFIG_FILE" | base64) + else + EMG_CONFIG_BASE64=$(cat "$EMG_CONFIG_FILE" | base64 -w 0) + fi + # Strip newlines + EMG_CONFIG_BASE64=$(echo "$EMG_CONFIG_BASE64" | tr -d '\n' | tr -d '\r') + + logInfo "Running: docker run -p 8000:8000 -d --name edgemicro_sanity_test ..." + docker run -d --name edgemicro_sanity_test \ + -p 8000:8000 \ + -e EDGEMICRO_ORG="$MOCHA_ORG" \ + -e EDGEMICRO_ENV="$MOCHA_ENV" \ + -e EDGEMICRO_KEY="$EMG_KEY" \ + -e EDGEMICRO_SECRET="$EMG_SECRET" \ + -e EDGEMICRO_CONFIG="$EMG_CONFIG_BASE64" \ + -e SERVICE_NAME=default \ + -e EDGEMICRO_PROCESSES=2 \ + "$image" > docker_run.log 2>&1 + + local result=$? + if [ $result -ne 0 ]; then + logError "Failed to start docker container" + cat docker_run.log + return 1 + fi + + logInfo "Waiting 15s for EMG to start inside docker container..." + sleep 15 + + # Check logs to see if it booted successfully + docker logs edgemicro_sanity_test > edgemicro_docker.logs 2>&1 + cat edgemicro_docker.logs | grep "PROCESS PID" > /dev/null 2>&1 + result=$? + if [ $result -eq 0 ]; then + logInfo "Successfully started EMG in Docker" + return 0 + else + logError "EMG inside Docker did not output PROCESS PID. Logs:" + cat edgemicro_docker.logs + return 1 + fi +} + +stopEMGDocker() { + logInfo "Stopping and removing EMG docker container..." + logInfo "=== CONTAINER LOGS ===" + docker logs edgemicro_sanity_test >> "$LOGFILE" 2>&1 + logInfo "======================" + docker stop edgemicro_sanity_test > /dev/null 2>&1 + docker rm edgemicro_sanity_test > /dev/null 2>&1 + rm -f edgemicro_docker.logs docker_run.log edgemicro.configure.txt + return 0 +} + +cleanDanglingResources() { + logInfo "Cleaning up dangling developers and apps..." + local devs + devs=$(curl -s -H "Authorization: Bearer $MOCHA_BEARER_TOKEN" \ + "https://api.enterprise.apigee.com/v1/organizations/${MOCHA_ORG}/developers" 2>/dev/null) + + if [ -z "$devs" ] || [[ "$devs" != "["* ]]; then + return 0 + fi + + for dev_email in $(echo "$devs" | jq -r '.[]' | grep "^edgemicro_dev_docker_sanity_"); do + logInfo "Deleting dangling developer: $dev_email" + + local apps + apps=$(curl -s -H "Authorization: Bearer $MOCHA_BEARER_TOKEN" \ + "https://api.enterprise.apigee.com/v1/organizations/${MOCHA_ORG}/developers/${dev_email}/apps" 2>/dev/null) + + if [ -n "$apps" ] && [[ "$apps" == "["* ]]; then + for app_name in $(echo "$apps" | jq -r '.[]'); do + logInfo "Deleting dangling developer app: $app_name for $dev_email" + curl -s -X DELETE -H "Authorization: Bearer $MOCHA_BEARER_TOKEN" \ + "https://api.enterprise.apigee.com/v1/organizations/${MOCHA_ORG}/developers/${dev_email}/apps/${app_name}" >/dev/null 2>&1 + done + fi + + curl -s -X DELETE -H "Authorization: Bearer $MOCHA_BEARER_TOKEN" \ + "https://api.enterprise.apigee.com/v1/organizations/${MOCHA_ORG}/developers/${dev_email}" >/dev/null 2>&1 + done +} + +teardown() { + echo "Tearing down and cleaning up resources..." + stopEMGDocker + cleanUp + + deleteDeveloperApp ${DEVELOPER_NAME} ${DEVELOPER_APP_NAME} >/dev/null 2>&1 + deleteAPIProduct ${PRODUCT_NAME} >/dev/null 2>&1 + undeployAPIProxy ${PROXY_NAME} ${MOCHA_ENV} ${proxyBundleVersion} >/dev/null 2>&1 + deleteAPIProxy ${PROXY_NAME} >/dev/null 2>&1 + deleteDeveloper ${DEVELOPER_NAME} >/dev/null 2>&1 + cleanDanglingResources + + rm -f "${DEVELOPER_APP_NAME}.json" + rm -f "${PRODUCT_NAME}.json" + rm -f "${PROXY_NAME}.zip" +} + +main() { + local result=0 + local ret=0 + local testCount=0 + local testPassCount=0 + local testFailCount=0 + + # Check required environment variables + if [ -z "$MOCHA_USER" ] || [ -z "$MOCHA_PASSWORD" ] || [ -z "$MOCHA_ORG" ] || [ -z "$MOCHA_ENV" ]; then + echo "MOCHA_USER, MOCHA_PASSWORD, MOCHA_ORG, and MOCHA_ENV must be set" + exit 1 + fi + + echo "Starting EMG Docker Sanity Test Suite against image: $DOCKER_IMAGE" + echo "Clean up previous state..." + teardown + + # 1. Provisioning Resources on Control Plane + echo "Provisioning resources on Apigee Edge..." + + createAPIProxy ${PROXY_NAME}; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to create API Proxy"; teardown; exit 1; fi + + createAPIProxyBundle ${PROXY_NAME}; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to bundle API Proxy"; teardown; exit 1; fi + + updateAPIProxy ${PROXY_NAME} ${PROXY_NAME}.zip ${proxyBundleVersion}; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to upload API Proxy bundle"; teardown; exit 1; fi + + deployAPIProxy ${PROXY_NAME} ${MOCHA_ENV} ${proxyBundleVersion}; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to deploy API Proxy"; teardown; exit 1; fi + + createAPIProduct ${PRODUCT_NAME} ${PROXY_NAME} "edgemicro-auth"; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to create API Product"; teardown; exit 1; fi + + createDeveloper ${DEVELOPER_NAME}; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to create Developer"; teardown; exit 1; fi + + createDeveloperApp ${DEVELOPER_NAME} ${DEVELOPER_APP_NAME} ${PRODUCT_NAME}; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to create Developer App"; teardown; exit 1; fi + + echo "Waiting 45s for Apigee Edge Control Plane propagation..." + sleep 45 + + # 2. Config generation + initEMG; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to init EMG config"; teardown; exit 1; fi + + configureEMG; ret=$? + if [ $ret -ne 0 ]; then echo "Failed to configure EMG"; teardown; exit 1; fi + + # Enable Quota plugin locally before base64 encoding it for the container + node setYamlVars ${EMG_CONFIG_FILE} 'edgemicro.plugins.sequence[1]' 'quota' > tmp_emg_file.yaml + cp tmp_emg_file.yaml ${EMG_CONFIG_FILE} + rm -f tmp_emg_file.yaml + + # 3. Start EMG in Docker + testCount=`expr $testCount + 1` + echo "$testCount) startEMG_in_docker" + startEMGDocker "$DOCKER_IMAGE"; ret=$? + if [ $ret -eq 0 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + + # 4. Happy Path API Key Test + testCount=`expr $testCount + 1` + echo "$testCount) testAPIProxy_with_apikey" + testAPIProxy; ret=$? + if [ $ret -eq 0 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + else + echo "$STATUS_FAIL_STR" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + + # 5. Invalid API Key Test + testCount=`expr $testCount + 1` + echo "$testCount) testInvalidAPIKey" + testInvalidAPIKey; ret=$? + if [ $ret -eq 0 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + else + echo "$STATUS_FAIL_STR" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + + # 6. OAuth Token Test + testCount=`expr $testCount + 1` + echo "$testCount) testAuthToken" + testAuthToken; ret=$? + if [ $ret -eq 0 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + else + echo "$STATUS_FAIL_STR" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + + # 7. Happy Path API Proxy with Bearer Token Test + testCount=`expr $testCount + 1` + echo "$testCount) testApiProxyWithAuthToken" + echo "Waiting 30s for OAuth token metadata synchronization..." + sleep 30 + testApiProxyWithAuthToken; ret=$? + if [ $ret -eq 0 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + else + echo "$STATUS_FAIL_STR" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + + # 8. Quota Enforcement Test + testCount=`expr $testCount + 1` + echo "$testCount) testQuota" + testQuota; ret=$? + if [ $ret -eq 0 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + else + echo "$STATUS_FAIL_STR" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + + # 9. Graceful Shutdown (SIGTERM) Test + testCount=`expr $testCount + 1` + echo "$testCount) graceful_shutdown_sigterm" + docker kill --signal=SIGTERM edgemicro_sanity_test > /dev/null 2>&1 + exit_code=$(docker wait edgemicro_sanity_test) + if [ "$exit_code" -eq 143 ]; then + echo "$STATUS_PASS_STR" + testPassCount=`expr $testPassCount + 1` + else + echo "$STATUS_FAIL_STR (exit code $exit_code)" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + else + echo "$STATUS_FAIL_STR" + result=1 + testFailCount=`expr $testFailCount + 1` + fi + + teardown + + echo + echo "$testCount tests, $testPassCount passed, $testFailCount failed" + exit $result +} + +main "$@"