diff --git a/_reference/localEmailViewer/README.md b/_reference/localEmailViewer/README.md index 238c2bf22..6492c1176 100644 --- a/_reference/localEmailViewer/README.md +++ b/_reference/localEmailViewer/README.md @@ -2,6 +2,7 @@ This app connects to your Docker LocalStack endpoints and gives you a compact in - SES generated emails - CloudWatch log groups, streams, and recent events +- Secrets Manager secrets and values ```shell npm start @@ -21,6 +22,8 @@ Features: attachment downloads, and new-message highlighting - CloudWatch Logs workspace with log group selection, stream filtering, adjustable time window, adjustable event limit, live refresh, and in-browser log search +- Secrets Manager workspace with live refresh, search, expandable secret metadata, lazy-loaded + secret values, and copyable JSON/text values - Compact single-page UI for switching between the local stack tools you use most Optional environment variables: @@ -35,4 +38,6 @@ CLOUDWATCH_VIEWER_REGION=ca-central-1 CLOUDWATCH_VIEWER_LOG_GROUP=development CLOUDWATCH_VIEWER_WINDOW_MS=900000 CLOUDWATCH_VIEWER_LIMIT=200 +SECRETS_VIEWER_ENDPOINT=http://localhost:4566 +SECRETS_VIEWER_REGION=ca-central-1 ``` diff --git a/_reference/localEmailViewer/index.js b/_reference/localEmailViewer/index.js index 162f9504b..f45eab624 100644 --- a/_reference/localEmailViewer/index.js +++ b/_reference/localEmailViewer/index.js @@ -6,6 +6,7 @@ import { DescribeLogStreamsCommand, FilterLogEventsCommand } from "@aws-sdk/client-cloudwatch-logs"; +import { GetSecretValueCommand, ListSecretsCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; import { simpleParser } from "mailparser"; const app = express(); @@ -19,6 +20,8 @@ const CLOUDWATCH_REGION = process.env.CLOUDWATCH_VIEWER_REGION || process.env.AW const CLOUDWATCH_DEFAULT_GROUP = process.env.CLOUDWATCH_VIEWER_LOG_GROUP || "development"; const CLOUDWATCH_DEFAULT_WINDOW_MS = Number(process.env.CLOUDWATCH_VIEWER_WINDOW_MS || 15 * 60 * 1000); const CLOUDWATCH_DEFAULT_LIMIT = Number(process.env.CLOUDWATCH_VIEWER_LIMIT || 200); +const SECRETS_ENDPOINT = process.env.SECRETS_VIEWER_ENDPOINT || CLOUDWATCH_ENDPOINT; +const SECRETS_REGION = process.env.SECRETS_VIEWER_REGION || CLOUDWATCH_REGION; const LOCALSTACK_CREDENTIALS = { accessKeyId: process.env.AWS_ACCESS_KEY_ID || "test", secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "test" @@ -28,6 +31,11 @@ const cloudWatchLogsClient = new CloudWatchLogsClient({ endpoint: CLOUDWATCH_ENDPOINT, credentials: LOCALSTACK_CREDENTIALS }); +const secretsManagerClient = new SecretsManagerClient({ + region: SECRETS_REGION, + endpoint: SECRETS_ENDPOINT, + credentials: LOCALSTACK_CREDENTIALS +}); app.use((req, res, next) => { res.set("Cache-Control", "no-store"); @@ -48,7 +56,8 @@ app.get("/health", (req, res) => { endpoint: SES_ENDPOINT, endpoints: { ses: SES_ENDPOINT, - cloudWatchLogs: CLOUDWATCH_ENDPOINT + cloudWatchLogs: CLOUDWATCH_ENDPOINT, + secretsManager: SECRETS_ENDPOINT }, port: PORT, defaultRefreshMs: DEFAULT_REFRESH_MS @@ -185,6 +194,48 @@ app.get("/api/logs/events", async (req, res) => { } }); +app.get("/api/secrets", async (req, res) => { + try { + res.json(await loadSecrets()); + } catch (error) { + console.error("Error fetching secrets:", error); + res.status(502).json({ + error: "Unable to fetch Secrets Manager secrets from LocalStack", + details: error.message, + endpoint: SECRETS_ENDPOINT + }); + } +}); + +app.get("/api/secrets/value", async (req, res) => { + try { + const secretId = String(req.query.id || ""); + + if (!secretId) { + res.status(400).json({ error: "Query parameter 'id' is required" }); + return; + } + + res.json(await loadSecretValue(secretId)); + } catch (error) { + if (error?.name === "ResourceNotFoundException") { + res.status(404).json({ + error: "Secret not found", + details: error.message, + endpoint: SECRETS_ENDPOINT + }); + return; + } + + console.error("Error fetching secret value:", error); + res.status(502).json({ + error: "Unable to fetch Secrets Manager value from LocalStack", + details: error.message, + endpoint: SECRETS_ENDPOINT + }); + } +}); + async function loadMessages() { const startedAt = Date.now(); const sesMessages = await fetchSesMessages(); @@ -352,6 +403,99 @@ async function loadLogEvents({ logGroupName, logStreamName, windowMs, limit }) { }; } +async function loadSecrets() { + const startedAt = Date.now(); + const secrets = []; + let nextToken; + let pageCount = 0; + + do { + const response = await secretsManagerClient.send( + new ListSecretsCommand({ + NextToken: nextToken, + MaxResults: 50 + }) + ); + + secrets.push( + ...(response.SecretList || []).map((secret, index) => ({ + id: secret.ARN || secret.Name || `secret-${index}`, + name: secret.Name || "Unnamed secret", + arn: secret.ARN || "", + description: secret.Description || "", + createdDate: normalizeTimestamp(secret.CreatedDate), + lastChangedDate: normalizeTimestamp(secret.LastChangedDate), + lastAccessedDate: normalizeTimestamp(secret.LastAccessedDate), + deletedDate: normalizeTimestamp(secret.DeletedDate), + primaryRegion: secret.PrimaryRegion || "", + owningService: secret.OwningService || "", + rotationEnabled: Boolean(secret.RotationEnabled), + versionCount: Object.keys(secret.SecretVersionsToStages || {}).length, + tagCount: Array.isArray(secret.Tags) ? secret.Tags.length : 0, + tags: (secret.Tags || []) + .map((tag) => ({ + key: tag.Key || "", + value: tag.Value || "" + })) + .filter((tag) => tag.key || tag.value) + })) + ); + + nextToken = response.NextToken; + pageCount += 1; + } while (nextToken && pageCount < 10 && secrets.length < 500); + + secrets.sort((left, right) => { + const leftTime = Date.parse(left.lastChangedDate || left.createdDate || 0) || 0; + const rightTime = Date.parse(right.lastChangedDate || right.createdDate || 0) || 0; + + if (rightTime !== leftTime) { + return rightTime - leftTime; + } + + return left.name.localeCompare(right.name); + }); + + return { + endpoint: SECRETS_ENDPOINT, + region: SECRETS_REGION, + fetchedAt: new Date().toISOString(), + fetchDurationMs: Date.now() - startedAt, + totalSecrets: secrets.length, + latestTimestamp: secrets[0]?.lastChangedDate || secrets[0]?.createdDate || "", + secrets + }; +} + +async function loadSecretValue(secretId) { + const startedAt = Date.now(); + const response = await secretsManagerClient.send( + new GetSecretValueCommand({ + SecretId: secretId + }) + ); + + const secretBinary = response.SecretBinary + ? typeof response.SecretBinary === "string" + ? response.SecretBinary + : Buffer.from(response.SecretBinary).toString("base64") + : ""; + + return { + endpoint: SECRETS_ENDPOINT, + region: SECRETS_REGION, + fetchDurationMs: Date.now() - startedAt, + id: secretId, + name: response.Name || "", + arn: response.ARN || "", + versionId: response.VersionId || "", + versionStages: Array.isArray(response.VersionStages) ? response.VersionStages : [], + createdDate: normalizeTimestamp(response.CreatedDate), + secretString: typeof response.SecretString === "string" ? response.SecretString : "", + secretBinary + }; +} + async function findSesMessageById(id) { const messages = await fetchSesMessages(); return messages.find((message, index) => resolveMessageId(message, index) === id) || null; @@ -569,6 +713,8 @@ function getClientConfig() { endpoint: SES_ENDPOINT, cloudWatchEndpoint: CLOUDWATCH_ENDPOINT, cloudWatchRegion: CLOUDWATCH_REGION, + secretsEndpoint: SECRETS_ENDPOINT, + secretsRegion: SECRETS_REGION, defaultLogGroup: CLOUDWATCH_DEFAULT_GROUP, defaultLogWindowMs: CLOUDWATCH_DEFAULT_WINDOW_MS, defaultLogLimit: CLOUDWATCH_DEFAULT_LIMIT @@ -596,6 +742,7 @@ function renderHtml() {
${escapeHtml(buildSecretPreview(secret))}
+${escapeHtml(message.textContent || "No plain-text content available for this message.")}`;
}
+ function renderSecretValuePanel(secret, valueState) {
+ if (!valueState) {
+ return `${valueState.displayHtml}
+ `;
+ }
+
function metaCard(label, value) {
return ``;
}
@@ -2035,7 +2740,6 @@ function clientApp(config) {
if (shouldOpen && !details.open) {
details.open = true;
- return;
}
if (!shouldOpen && details.open) {
@@ -2053,6 +2757,31 @@ function clientApp(config) {
window.requestAnimationFrame(applyCardState);
}
+ function syncSecretExpansion() {
+ const applySecretState = () => {
+ el.secretsList.querySelectorAll(".secretCard").forEach((details) => {
+ const id = details.dataset.id;
+ const shouldOpen = Boolean(id && secretsState.openIds.has(id));
+
+ if (shouldOpen && !details.open) {
+ details.open = true;
+ }
+
+ if (!shouldOpen && details.open) {
+ details.open = false;
+ return;
+ }
+
+ if (shouldOpen) {
+ ensureSecretValue(id, { force: false });
+ }
+ });
+ };
+
+ applySecretState();
+ window.requestAnimationFrame(applySecretState);
+ }
+
function resolveAttachmentIcon(attachment) {
const filename = String(attachment?.filename || "").toLowerCase();
const contentType = String(attachment?.contentType || "").toLowerCase();
@@ -2137,6 +2866,10 @@ function clientApp(config) {
return logsState.events.find((event) => event.id === id);
}
+ function getSecret(id) {
+ return secretsState.items.find((secret) => secret.id === id);
+ }
+
async function loadRaw(id) {
if (state.raw[id]?.status === "loaded") {
return state.raw[id].value;
@@ -2168,6 +2901,72 @@ function clientApp(config) {
}
}
+ async function ensureSecretValue(id, options = {}) {
+ const { force = false } = options;
+
+ if (!id) {
+ return null;
+ }
+
+ if (!force && secretsState.values[id]?.status === "loaded") {
+ return secretsState.values[id];
+ }
+
+ if (secretsState.values[id]?.status === "loading") {
+ return null;
+ }
+
+ secretsState.values[id] = { status: "loading" };
+ renderSecretsAll();
+
+ try {
+ const response = await fetch(`/api/secrets/value?id=${encodeURIComponent(id)}`, { cache: "no-store" });
+
+ if (!response.ok) {
+ const payload = await safeJson(response);
+ throw new Error(payload?.details || payload?.error || `Request failed with ${response.status}`);
+ }
+
+ const payload = await response.json();
+ const secretString = typeof payload.secretString === "string" ? payload.secretString : "";
+ const secretBinary = typeof payload.secretBinary === "string" ? payload.secretBinary : "";
+ const parsedString = secretString ? tryParseJsonText(secretString) : { ok: false, value: null };
+ const entry = {
+ status: "loaded",
+ label: secretBinary ? "Binary" : parsedString.ok ? "JSON" : secretString ? "Text" : "Empty",
+ copyValue: secretBinary
+ ? secretBinary
+ : parsedString.ok
+ ? JSON.stringify(parsedString.value, null, 2)
+ : secretString || "No secret value.",
+ displayHtml: secretBinary
+ ? escapeHtml(secretBinary)
+ : parsedString.ok
+ ? highlightJsonText(JSON.stringify(parsedString.value, null, 2))
+ : escapeHtml(secretString || "No secret value."),
+ isJson: parsedString.ok,
+ isBinary: Boolean(secretBinary),
+ versionId: payload.versionId || "",
+ versionStages: Array.isArray(payload.versionStages) ? payload.versionStages : [],
+ createdDate: payload.createdDate || "",
+ arn: payload.arn || "",
+ name: payload.name || ""
+ };
+
+ secretsState.values[id] = entry;
+ return entry;
+ } catch (error) {
+ secretsState.values[id] = {
+ status: "error",
+ error: error.message || "Unknown secret value error"
+ };
+ setSecretsStatus("Could not load the secret value.", "bad");
+ return null;
+ } finally {
+ renderSecretsAll();
+ }
+ }
+
async function copyText(value) {
try {
await navigator.clipboard.writeText(value);
@@ -2204,9 +3003,19 @@ function clientApp(config) {
el.logsStatusChip.textContent = message;
}
+ function setSecretsStatus(message, tone) {
+ el.secretsStatusChip.className = "status";
+
+ if (tone) {
+ el.secretsStatusChip.classList.add(tone);
+ }
+
+ el.secretsStatusChip.textContent = message;
+ }
+
function getInitialPanel() {
const storedPanel = readStoredValue(PANEL_STORAGE_KEY);
- return storedPanel === "logs" ? "logs" : "emails";
+ return ["emails", "logs", "secrets"].includes(storedPanel) ? storedPanel : "emails";
}
function getInitialTheme() {
@@ -2267,6 +3076,14 @@ function clientApp(config) {
});
}
+ function persistSecretPreferences() {
+ writeStoredJson(SECRET_PREFERENCES_STORAGE_KEY, {
+ search: secretsState.search,
+ auto: secretsState.auto,
+ interval: secretsState.interval
+ });
+ }
+
function getStoredPreferences(key) {
try {
const rawValue = window.localStorage.getItem(key);
@@ -2395,8 +3212,34 @@ function clientApp(config) {
}
}
+ function buildSecretPreview(secret) {
+ const fragments = [];
+
+ if (secret.description) {
+ fragments.push(secret.description);
+ }
+
+ if (secret.owningService) {
+ fragments.push(`Service: ${secret.owningService}`);
+ }
+
+ if (secret.primaryRegion) {
+ fragments.push(`Region: ${secret.primaryRegion}`);
+ }
+
+ if (secret.tagCount) {
+ fragments.push(`${secret.tagCount} tag${secret.tagCount === 1 ? "" : "s"}`);
+ }
+
+ if (!fragments.length) {
+ fragments.push("No description or tags yet.");
+ }
+
+ return fragments.join(" • ");
+ }
+
function renderLogPreviewContent(event) {
- const parsedLog = tryParseLogJson(event?.message);
+ const parsedLog = tryParseJsonText(event?.message);
if (!parsedLog.ok) {
return escapeHtml(event?.preview || "No preview available.");
@@ -2408,7 +3251,7 @@ function clientApp(config) {
}
function renderLogBodyContent(message) {
- const parsedLog = tryParseLogJson(message);
+ const parsedLog = tryParseJsonText(message);
if (!parsedLog.ok) {
return escapeHtml(formatLogMessage(message));
@@ -2417,7 +3260,7 @@ function clientApp(config) {
return highlightJsonText(JSON.stringify(parsedLog.value, null, 2));
}
- function tryParseLogJson(message) {
+ function tryParseJsonText(message) {
const value = String(message || "").trim();
if (!value) {
@@ -2485,4 +3328,5 @@ app.listen(PORT, () => {
console.log(`LocalStack inspector is running on http://localhost:${PORT}`);
console.log(`Watching LocalStack SES endpoint at ${SES_ENDPOINT}`);
console.log(`Watching LocalStack CloudWatch Logs endpoint at ${CLOUDWATCH_ENDPOINT} (${CLOUDWATCH_REGION})`);
+ console.log(`Watching LocalStack Secrets Manager endpoint at ${SECRETS_ENDPOINT} (${SECRETS_REGION})`);
});
diff --git a/_reference/localEmailViewer/package-lock.json b/_reference/localEmailViewer/package-lock.json
index 2dec2be60..2f00ef450 100644
--- a/_reference/localEmailViewer/package-lock.json
+++ b/_reference/localEmailViewer/package-lock.json
@@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.1012.0",
+ "@aws-sdk/client-secrets-manager": "^3.1013.0",
"express": "^5.1.0",
"mailparser": "^3.7.4",
"node-fetch": "^3.3.2"
@@ -207,14 +208,64 @@
"node": ">=20.0.0"
}
},
+ "node_modules/@aws-sdk/client-secrets-manager": {
+ "version": "3.1013.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.1013.0.tgz",
+ "integrity": "sha512-jVleaMhnwz7JsYZ8vo3otRdhNl1vmJPfwYQtAfb0T29TwfI9pvfRuWD4CXbm0DPmCJzhN7YT+tqn1dLhk83Y3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "^3.973.22",
+ "@aws-sdk/credential-provider-node": "^3.972.23",
+ "@aws-sdk/middleware-host-header": "^3.972.8",
+ "@aws-sdk/middleware-logger": "^3.972.8",
+ "@aws-sdk/middleware-recursion-detection": "^3.972.8",
+ "@aws-sdk/middleware-user-agent": "^3.972.23",
+ "@aws-sdk/region-config-resolver": "^3.972.8",
+ "@aws-sdk/types": "^3.973.6",
+ "@aws-sdk/util-endpoints": "^3.996.5",
+ "@aws-sdk/util-user-agent-browser": "^3.972.8",
+ "@aws-sdk/util-user-agent-node": "^3.973.9",
+ "@smithy/config-resolver": "^4.4.11",
+ "@smithy/core": "^3.23.12",
+ "@smithy/fetch-http-handler": "^5.3.15",
+ "@smithy/hash-node": "^4.2.12",
+ "@smithy/invalid-dependency": "^4.2.12",
+ "@smithy/middleware-content-length": "^4.2.12",
+ "@smithy/middleware-endpoint": "^4.4.26",
+ "@smithy/middleware-retry": "^4.4.43",
+ "@smithy/middleware-serde": "^4.2.15",
+ "@smithy/middleware-stack": "^4.2.12",
+ "@smithy/node-config-provider": "^4.3.12",
+ "@smithy/node-http-handler": "^4.5.0",
+ "@smithy/protocol-http": "^5.3.12",
+ "@smithy/smithy-client": "^4.12.6",
+ "@smithy/types": "^4.13.1",
+ "@smithy/url-parser": "^4.2.12",
+ "@smithy/util-base64": "^4.3.2",
+ "@smithy/util-body-length-browser": "^4.2.2",
+ "@smithy/util-body-length-node": "^4.2.3",
+ "@smithy/util-defaults-mode-browser": "^4.3.42",
+ "@smithy/util-defaults-mode-node": "^4.2.45",
+ "@smithy/util-endpoints": "^3.3.3",
+ "@smithy/util-middleware": "^4.2.12",
+ "@smithy/util-retry": "^4.2.12",
+ "@smithy/util-utf8": "^4.2.2",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/@aws-sdk/core": {
- "version": "3.973.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.21.tgz",
- "integrity": "sha512-OTUcDX9Yfz/FLKbHjiMaP9D4Hs44lYJzN7zBcrK2nDmBt0Wr8D6nYt12QoBkZsW0nVMFsTIGaZCrsU9zCcIMXQ==",
+ "version": "3.973.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.22.tgz",
+ "integrity": "sha512-lY6g5L95jBNgOUitUhfV2N/W+i08jHEl3xuLODYSQH5Sf50V+LkVYBSyZRLtv2RyuXZXiV7yQ+acpswK1tlrOA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "^3.973.6",
- "@aws-sdk/xml-builder": "^3.972.12",
+ "@aws-sdk/xml-builder": "^3.972.14",
"@smithy/core": "^3.23.12",
"@smithy/node-config-provider": "^4.3.12",
"@smithy/property-provider": "^4.2.12",
@@ -232,12 +283,12 @@
}
},
"node_modules/@aws-sdk/credential-provider-env": {
- "version": "3.972.19",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.19.tgz",
- "integrity": "sha512-33NpkQtmnsjLr9QdZvL3w8bjy+WoBJ+jY8JwuzxIq38rDNi1kwpBWW7Yjh+8bMlksd+ZAWW0fH4S/6OeoAdU5A==",
+ "version": "3.972.20",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.20.tgz",
+ "integrity": "sha512-vI0QN96DFx3g9AunfOWF3CS4cMkqFiR/WM/FyP9QHr5rZ2dKPkYwP3tCgAOvGuu9CXI7dC1vU2FVUuZ+tfpNvQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/core": "^3.973.22",
"@aws-sdk/types": "^3.973.6",
"@smithy/property-provider": "^4.2.12",
"@smithy/types": "^4.13.1",
@@ -248,12 +299,12 @@
}
},
"node_modules/@aws-sdk/credential-provider-http": {
- "version": "3.972.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.21.tgz",
- "integrity": "sha512-xFke7yjbON4unNOG0TApQwz+o1LH5VhVLgWlUuiLRWNDyBfeHIFje2ck8qHybvJ8Fkm5m3SsN+pvHtVo6PGWlQ==",
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.22.tgz",
+ "integrity": "sha512-aS/81smalpe7XDnuQfOq4LIPuaV2PRKU2aMTrHcqO5BD4HwO5kESOHNcec2AYfBtLtIDqgF6RXisgBnfK/jt0w==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/core": "^3.973.22",
"@aws-sdk/types": "^3.973.6",
"@smithy/fetch-http-handler": "^5.3.15",
"@smithy/node-http-handler": "^4.5.0",
@@ -269,19 +320,19 @@
}
},
"node_modules/@aws-sdk/credential-provider-ini": {
- "version": "3.972.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.21.tgz",
- "integrity": "sha512-fmJN7KhB7CoG65w9fC2LVOd2wZbR2d1yJIpZNe2J5CeDPu7nUHSmavuJAeGEoE3OL5UIBVPNhmK/fV/NQrs3Hw==",
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.22.tgz",
+ "integrity": "sha512-rpF8fBT0LllMDp78s62aL2A/8MaccjyJ0ORzqu+ZADeECLSrrCWIeeXsuRam+pxiAMkI1uIyDZJmgLGdadkPXw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
- "@aws-sdk/credential-provider-env": "^3.972.19",
- "@aws-sdk/credential-provider-http": "^3.972.21",
- "@aws-sdk/credential-provider-login": "^3.972.21",
- "@aws-sdk/credential-provider-process": "^3.972.19",
- "@aws-sdk/credential-provider-sso": "^3.972.21",
- "@aws-sdk/credential-provider-web-identity": "^3.972.21",
- "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/core": "^3.973.22",
+ "@aws-sdk/credential-provider-env": "^3.972.20",
+ "@aws-sdk/credential-provider-http": "^3.972.22",
+ "@aws-sdk/credential-provider-login": "^3.972.22",
+ "@aws-sdk/credential-provider-process": "^3.972.20",
+ "@aws-sdk/credential-provider-sso": "^3.972.22",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.22",
+ "@aws-sdk/nested-clients": "^3.996.12",
"@aws-sdk/types": "^3.973.6",
"@smithy/credential-provider-imds": "^4.2.12",
"@smithy/property-provider": "^4.2.12",
@@ -294,13 +345,13 @@
}
},
"node_modules/@aws-sdk/credential-provider-login": {
- "version": "3.972.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.21.tgz",
- "integrity": "sha512-ENU+YCiuQocQjfIf9bPxZ+ZY0wIBkl3SMH22optBQwy8UFpSfonHynXzGT27xQxer4cYTNOpwDqbfo57BusbpQ==",
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.22.tgz",
+ "integrity": "sha512-u33CO9zeNznlVSg9tWTCRYxaGkqr1ufU6qeClpmzAabXZa8RZxQoVXxL5T53oZJFzQYj+FImORCSsi7H7B77gQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
- "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/core": "^3.973.22",
+ "@aws-sdk/nested-clients": "^3.996.12",
"@aws-sdk/types": "^3.973.6",
"@smithy/property-provider": "^4.2.12",
"@smithy/protocol-http": "^5.3.12",
@@ -313,17 +364,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-node": {
- "version": "3.972.22",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.22.tgz",
- "integrity": "sha512-VE6i8nkmrRyhKut7nnfCWRbdDf+CfyRr8ixSwdaPDguYlgvkAO2pHu9oK11XzbSuatB0io1ozI/vpYhelXn8Pg==",
+ "version": "3.972.23",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.23.tgz",
+ "integrity": "sha512-U8tyLbLOZItuVWTH0ay9gWo4xMqZwqQbg1oMzdU4FQSkTpqXemm4X0uoKBR6llqAStgBp30ziKFJHTA43l4qMw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/credential-provider-env": "^3.972.19",
- "@aws-sdk/credential-provider-http": "^3.972.21",
- "@aws-sdk/credential-provider-ini": "^3.972.21",
- "@aws-sdk/credential-provider-process": "^3.972.19",
- "@aws-sdk/credential-provider-sso": "^3.972.21",
- "@aws-sdk/credential-provider-web-identity": "^3.972.21",
+ "@aws-sdk/credential-provider-env": "^3.972.20",
+ "@aws-sdk/credential-provider-http": "^3.972.22",
+ "@aws-sdk/credential-provider-ini": "^3.972.22",
+ "@aws-sdk/credential-provider-process": "^3.972.20",
+ "@aws-sdk/credential-provider-sso": "^3.972.22",
+ "@aws-sdk/credential-provider-web-identity": "^3.972.22",
"@aws-sdk/types": "^3.973.6",
"@smithy/credential-provider-imds": "^4.2.12",
"@smithy/property-provider": "^4.2.12",
@@ -336,12 +387,12 @@
}
},
"node_modules/@aws-sdk/credential-provider-process": {
- "version": "3.972.19",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.19.tgz",
- "integrity": "sha512-hjj5bFo4kf5/WzAMjDEFByVOMbq5gZiagIpJexf7Kp9nIDaGzhCphMsx03NCA8s9zUJzHlD1lXazd7MS+e03Lg==",
+ "version": "3.972.20",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.20.tgz",
+ "integrity": "sha512-QRfk7GbA4/HDRjhP3QYR6QBr/QKreVoOzvvlRHnOuGgYJkeoPgPY3LAI1kK1ZMgZ4hH9KiGp757/ntol+INAig==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/core": "^3.973.22",
"@aws-sdk/types": "^3.973.6",
"@smithy/property-provider": "^4.2.12",
"@smithy/shared-ini-file-loader": "^4.4.7",
@@ -353,14 +404,14 @@
}
},
"node_modules/@aws-sdk/credential-provider-sso": {
- "version": "3.972.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.21.tgz",
- "integrity": "sha512-9jWRCuMZpZKlqCZ46bvievqdfswsyB2yPAr9rOiN+FxaGgf8jrR5iYDqJgscvk1jrbAxiK4cIjHv3XjIAWAhzQ==",
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.22.tgz",
+ "integrity": "sha512-4vqlSaUbBj4aNPVKfB6yXuIQ2Z2mvLfIGba2OzzF6zUkN437/PGWsxBU2F8QPSFHti6seckvyCXidU3H+R8NvQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
- "@aws-sdk/nested-clients": "^3.996.11",
- "@aws-sdk/token-providers": "3.1012.0",
+ "@aws-sdk/core": "^3.973.22",
+ "@aws-sdk/nested-clients": "^3.996.12",
+ "@aws-sdk/token-providers": "3.1013.0",
"@aws-sdk/types": "^3.973.6",
"@smithy/property-provider": "^4.2.12",
"@smithy/shared-ini-file-loader": "^4.4.7",
@@ -372,13 +423,13 @@
}
},
"node_modules/@aws-sdk/credential-provider-web-identity": {
- "version": "3.972.21",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.21.tgz",
- "integrity": "sha512-ShWQO/cQVZ+j3zUDK7Kj+m7grPzQCVA2iaZdJ+hJTGvVH5lR32Ip/rgZZ+zBdH6D6wczP9Upa4NMXoqJdGpK1g==",
+ "version": "3.972.22",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.22.tgz",
+ "integrity": "sha512-/wN1CYg2rVLhW8/jLxMWacQrkpaynnL+4j/Z+e6X1PfoE6NiC0BeOw3i0JmtZrKun85wNV5GmspvuWJihfeeUw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
- "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/core": "^3.973.22",
+ "@aws-sdk/nested-clients": "^3.996.12",
"@aws-sdk/types": "^3.973.6",
"@smithy/property-provider": "^4.2.12",
"@smithy/shared-ini-file-loader": "^4.4.7",
@@ -435,12 +486,12 @@
}
},
"node_modules/@aws-sdk/middleware-user-agent": {
- "version": "3.972.22",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.22.tgz",
- "integrity": "sha512-pZPNGWZVQvgUIO/P9PXZNz7ciq9mLYb/wQEurg3phKTa3DiBIunIRcgA0eBNwmog6S3oy0KR1bv4EJ4ld9A5sQ==",
+ "version": "3.972.23",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.23.tgz",
+ "integrity": "sha512-HQu8QoqGZZTvg0Spl9H39QTsSMFwgu+8yz/QGKndXFLk9FZMiCiIgBCVlTVKMDvVbgqIzD9ig+/HmXsIL2Rb+g==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/core": "^3.973.22",
"@aws-sdk/types": "^3.973.6",
"@aws-sdk/util-endpoints": "^3.996.5",
"@smithy/core": "^3.23.12",
@@ -454,23 +505,23 @@
}
},
"node_modules/@aws-sdk/nested-clients": {
- "version": "3.996.11",
- "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.11.tgz",
- "integrity": "sha512-i7SwoSR4JB/79JoGDUACnFUQOZwXGLWNX35lIb1Pq72nUGlVV+RFZp+BLa8S+mog2pbXU9+6Kc5YwGiMi5bKhQ==",
+ "version": "3.996.12",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.12.tgz",
+ "integrity": "sha512-KLdQGJPSm98uLINolQ0Tol8OAbk7g0Y7zplHJ1K83vbMIH13aoCvR6Tho66xueW4l4aZlEgVGLWBnD8ifUMsGQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "^3.973.21",
+ "@aws-sdk/core": "^3.973.22",
"@aws-sdk/middleware-host-header": "^3.972.8",
"@aws-sdk/middleware-logger": "^3.972.8",
"@aws-sdk/middleware-recursion-detection": "^3.972.8",
- "@aws-sdk/middleware-user-agent": "^3.972.22",
+ "@aws-sdk/middleware-user-agent": "^3.972.23",
"@aws-sdk/region-config-resolver": "^3.972.8",
"@aws-sdk/types": "^3.973.6",
"@aws-sdk/util-endpoints": "^3.996.5",
"@aws-sdk/util-user-agent-browser": "^3.972.8",
- "@aws-sdk/util-user-agent-node": "^3.973.8",
+ "@aws-sdk/util-user-agent-node": "^3.973.9",
"@smithy/config-resolver": "^4.4.11",
"@smithy/core": "^3.23.12",
"@smithy/fetch-http-handler": "^5.3.15",
@@ -519,13 +570,13 @@
}
},
"node_modules/@aws-sdk/token-providers": {
- "version": "3.1012.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1012.0.tgz",
- "integrity": "sha512-vzKwy020zjuiF4WTJzejx5nYcXJnRhHpb6i3lyZHIwfFwXG1yX4bzBVNMWYWF+bz1i2Pp2VhJbPyzpqj4VuJXQ==",
+ "version": "3.1013.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1013.0.tgz",
+ "integrity": "sha512-IL1c54UvbuERrs9oLm5rvkzMciwhhpn1FL0SlC3XUMoLlFhdBsWJgQKK8O5fsQLxbFVqjbjFx9OBkrn44X9PHw==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/core": "^3.973.21",
- "@aws-sdk/nested-clients": "^3.996.11",
+ "@aws-sdk/core": "^3.973.22",
+ "@aws-sdk/nested-clients": "^3.996.12",
"@aws-sdk/types": "^3.973.6",
"@smithy/property-provider": "^4.2.12",
"@smithy/shared-ini-file-loader": "^4.4.7",
@@ -590,12 +641,12 @@
}
},
"node_modules/@aws-sdk/util-user-agent-node": {
- "version": "3.973.8",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.8.tgz",
- "integrity": "sha512-Kvb96TafGPLYo4Z2GRCzQTne77epXgiZEo0DDXwavzkWmgDV/1XD1tMA766gzRcHHFUraWsE+4T8DKtPTZUxgQ==",
+ "version": "3.973.9",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.9.tgz",
+ "integrity": "sha512-jeFqqp8KD/P5O+qeKxyGeu7WEVIZFNprnkaDjGmBOjwxYwafCBhpxTgV1TlW6L8e76Vh/siNylNmN/OmSIFBUQ==",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/middleware-user-agent": "^3.972.22",
+ "@aws-sdk/middleware-user-agent": "^3.972.23",
"@aws-sdk/types": "^3.973.6",
"@smithy/node-config-provider": "^4.3.12",
"@smithy/types": "^4.13.1",
@@ -615,9 +666,9 @@
}
},
"node_modules/@aws-sdk/xml-builder": {
- "version": "3.972.13",
- "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.13.tgz",
- "integrity": "sha512-I/+BMxM4WE/6xL0tyV7tAUDOAXmyw/va1oGr/eSly43HmLUcD1G+v96vEKAA8VoLcZ03ZQo/PWzjmN9zQErqPQ==",
+ "version": "3.972.14",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.14.tgz",
+ "integrity": "sha512-G/Yd8Bnnyh8QrqLf8jWJbixEnScUFW24e/wOBGYdw1Cl4r80KX/DvHyM2GVZ2vTp7J4gTEr8IXJlTadA8+UfuQ==",
"license": "Apache-2.0",
"dependencies": {
"@smithy/types": "^4.13.1",
diff --git a/_reference/localEmailViewer/package.json b/_reference/localEmailViewer/package.json
index 377708878..67ea3fc6b 100644
--- a/_reference/localEmailViewer/package.json
+++ b/_reference/localEmailViewer/package.json
@@ -10,9 +10,10 @@
"keywords": [],
"author": "",
"license": "ISC",
- "description": "LocalStack inspector for SES emails and CloudWatch logs",
+ "description": "LocalStack inspector for SES emails, CloudWatch logs, and Secrets Manager",
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.1012.0",
+ "@aws-sdk/client-secrets-manager": "^3.1013.0",
"express": "^5.1.0",
"mailparser": "^3.7.4",
"node-fetch": "^3.3.2"