Compare commits

..

4 Commits

Author SHA1 Message Date
fdc2420238 5.3.3
Some checks failed
Default (tags) / security (push) Successful in 36s
Default (tags) / test (push) Successful in 1m3s
Default (tags) / release (push) Failing after 1m4s
Default (tags) / metadata (push) Successful in 1m13s
2025-08-16 13:15:32 +00:00
e3a76ca577 fix(appdata): Redact sensitive values in AppData logs and add redaction tests 2025-08-16 13:15:32 +00:00
2cc0da4462 5.3.2
Some checks failed
Default (tags) / security (push) Successful in 40s
Default (tags) / test (push) Failing after 4m3s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-16 12:42:51 +00:00
b1e4ab09db fix(dependencies): Bump @push.rocks/qenv to ^6.1.3 and add local Claude settings 2025-08-16 12:42:51 +00:00
6 changed files with 174 additions and 29 deletions

View File

@@ -1,5 +1,20 @@
# Changelog # Changelog
## 2025-08-16 - 5.3.3 - fix(appdata)
Redact sensitive values in AppData logs and add redaction tests
- Add redactSensitiveValue helper in AppData to mask secrets (API keys, tokens, passwords, JWTs, etc.) during logging.
- Use redaction when logging raw and final mapping values in processMappingValue and nested key logging to avoid leaking sensitive data.
- Improve log output for long or special values (JWT/base64 detection, length-aware previews) while preserving actual stored values.
- Add test/test.redaction.ts to verify sensitive environment values are redacted in console output but still stored correctly in the kv store.
- Add local config .claude/settings.local.json (editor/CI permissions/settings).
## 2025-08-16 - 5.3.2 - fix(dependencies)
Bump @push.rocks/qenv to ^6.1.3 and add local Claude settings
- Bumped dependency @push.rocks/qenv from ^6.1.2 to ^6.1.3 in package.json
- Added .claude/settings.local.json to provide local Claude permissions and tooling allowances for development/CI helpers
## 2025-08-15 - 5.3.1 - fix(AppData/Conversion) ## 2025-08-15 - 5.3.1 - fix(AppData/Conversion)
Improve boolean conversion and mapping evaluation in AppData, ensuring falsy values (like false, 0, and empty strings) are correctly handled and logged. Also, reduce test timeout and add local permissions settings for development. Improve boolean conversion and mapping evaluation in AppData, ensuring falsy values (like false, 0, and empty strings) are correctly handled and logged. Also, reduce test timeout and add local permissions settings for development.

View File

@@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/npmextra", "name": "@push.rocks/npmextra",
"version": "5.3.1", "version": "5.3.3",
"private": false, "private": false,
"description": "A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.", "description": "A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
@@ -21,7 +21,7 @@
}, },
"homepage": "https://code.foss.global/push.rocks/npmextra#readme", "homepage": "https://code.foss.global/push.rocks/npmextra#readme",
"dependencies": { "dependencies": {
"@push.rocks/qenv": "^6.1.2", "@push.rocks/qenv": "^6.1.3",
"@push.rocks/smartfile": "^11.2.5", "@push.rocks/smartfile": "^11.2.5",
"@push.rocks/smartjson": "^5.0.20", "@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartlog": "^3.1.8", "@push.rocks/smartlog": "^3.1.8",

42
pnpm-lock.yaml generated
View File

@@ -9,8 +9,8 @@ importers:
.: .:
dependencies: dependencies:
'@push.rocks/qenv': '@push.rocks/qenv':
specifier: ^6.1.2 specifier: ^6.1.3
version: 6.1.2 version: 6.1.3
'@push.rocks/smartfile': '@push.rocks/smartfile':
specifier: ^11.2.5 specifier: ^11.2.5
version: 11.2.5 version: 11.2.5
@@ -244,8 +244,8 @@ packages:
'@borewit/text-codec@0.1.1': '@borewit/text-codec@0.1.1':
resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==}
'@cloudflare/workers-types@4.20250813.0': '@cloudflare/workers-types@4.20250816.0':
resolution: {integrity: sha512-RFFjomDndGR+p7ug1HWDlW21qOJyRZbmI99dUtuR9tmwJbSZhUUnSFmzok9lBYVfkMMrO1O5vmB+IlgiecgLEA==} resolution: {integrity: sha512-R9ADrrINo1CqTwCddH39Tjlsc3grim6KeO7l8yddNbldH3uTkaAXYCzO0WiyLG7irLzLDrZVc4tLhN6BO3tdFw==}
'@colors/colors@1.6.0': '@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
@@ -578,8 +578,8 @@ packages:
'@push.rocks/mongodump@1.0.8': '@push.rocks/mongodump@1.0.8':
resolution: {integrity: sha512-oDufyjNBg8I50OaJvbHhc0RnRpJQ544dr9her0G6sA8JmI3hD2/amTdcPLVIX1kzYf5GsTUKeWuRaZgdNqz3ew==} resolution: {integrity: sha512-oDufyjNBg8I50OaJvbHhc0RnRpJQ544dr9her0G6sA8JmI3hD2/amTdcPLVIX1kzYf5GsTUKeWuRaZgdNqz3ew==}
'@push.rocks/qenv@6.1.2': '@push.rocks/qenv@6.1.3':
resolution: {integrity: sha512-epb5Ey7E3jVCjxvNmQ5bcjPs9+7d1z/5bV/V8+qwrPqZrbgXnslOnsQWOh9usAatO0VJqqZmSvLYSpjnm3NEcA==} resolution: {integrity: sha512-+z2hsAU/7CIgpYLFqvda8cn9rUBMHqLdQLjsFfRn5jPoD7dJ5rFlpkbhfM4Ws8mHMniwWaxGKo+q/YBhtzRBLg==}
'@push.rocks/smartarchive@3.0.8': '@push.rocks/smartarchive@3.0.8':
resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==} resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==}
@@ -722,8 +722,8 @@ packages:
'@push.rocks/smarts3@2.2.5': '@push.rocks/smarts3@2.2.5':
resolution: {integrity: sha512-OZjD0jBCUTJCLnwraxBcyZ3he5buXf2OEM1zipiTBChA2EcKUZWKk/a6KR5WT+NlFCIIuB23UG+U+cxsIWM91Q==} resolution: {integrity: sha512-OZjD0jBCUTJCLnwraxBcyZ3he5buXf2OEM1zipiTBChA2EcKUZWKk/a6KR5WT+NlFCIIuB23UG+U+cxsIWM91Q==}
'@push.rocks/smartshell@3.2.3': '@push.rocks/smartshell@3.2.4':
resolution: {integrity: sha512-BWA/DH1H9lG7Er23d4uYgirfYaya5dX4g/WpWm2la7mOzuL9o2FnPIhel52DQUKIh7ty3Ql305ApV8YaAb4+/w==} resolution: {integrity: sha512-zZEKfRl3qBaII9BJULk4rB/+EelUpgM2/qHCQLui7c4589HTge4o0nWn+olFw/Hge88qUO77OK1sN7hQFZ6zeg==}
'@push.rocks/smartsitemap@2.0.3': '@push.rocks/smartsitemap@2.0.3':
resolution: {integrity: sha512-jIcms8V1b2mt3dS4PKNlLR1DRC8pCDWMRVbnyM/2+snZOJZonQRlQzAyX8No0EfLbfdrfnxv2IjPX13X29Re6g==} resolution: {integrity: sha512-jIcms8V1b2mt3dS4PKNlLR1DRC8pCDWMRVbnyM/2+snZOJZonQRlQzAyX8No0EfLbfdrfnxv2IjPX13X29Re6g==}
@@ -734,8 +734,8 @@ packages:
'@push.rocks/smartspawn@3.0.3': '@push.rocks/smartspawn@3.0.3':
resolution: {integrity: sha512-DyrGPV69wwOiJgKkyruk5hS3UEGZ99xFAqBE9O2nM8VXCRLbbty3xt1Ug5Z092ZZmJYaaGMSnMw3ijyZJFCT0Q==} resolution: {integrity: sha512-DyrGPV69wwOiJgKkyruk5hS3UEGZ99xFAqBE9O2nM8VXCRLbbty3xt1Ug5Z092ZZmJYaaGMSnMw3ijyZJFCT0Q==}
'@push.rocks/smartstate@2.0.25': '@push.rocks/smartstate@2.0.26':
resolution: {integrity: sha512-gWmbDCx5esezHDQnD2nOClxeTiWtvU1wEdP0XbheCcXzaGEv0H8apRjQBksRZJd9FC3ezrJU00GLh0eH9rPyMQ==} resolution: {integrity: sha512-lMcf0ZWWs9jej9wjapuonuIZiQNiD9NcAcvRDFXq7GtQf/HUyr6zr5K1XxGZaCIGyYrbYnBHBpNU+8DBoarHrA==}
'@push.rocks/smartstream@2.0.8': '@push.rocks/smartstream@2.0.8':
resolution: {integrity: sha512-GlF/9cCkvBHwKa3DK4DO5wjfSgqkj6gAS4TrY9uD5NMHu9RQv4WiNrElTYj7iCEpnZgUnLO3tzw1JA3NRIMnnA==} resolution: {integrity: sha512-GlF/9cCkvBHwKa3DK4DO5wjfSgqkj6gAS4TrY9uD5NMHu9RQv4WiNrElTYj7iCEpnZgUnLO3tzw1JA3NRIMnnA==}
@@ -3890,7 +3890,7 @@ snapshots:
'@api.global/typedrequest': 3.1.10 '@api.global/typedrequest': 3.1.10
'@api.global/typedrequest-interfaces': 3.0.19 '@api.global/typedrequest-interfaces': 3.0.19
'@api.global/typedsocket': 3.0.1 '@api.global/typedsocket': 3.0.1
'@cloudflare/workers-types': 4.20250813.0 '@cloudflare/workers-types': 4.20250816.0
'@design.estate/dees-comms': 1.0.27 '@design.estate/dees-comms': 1.0.27
'@push.rocks/lik': 6.2.2 '@push.rocks/lik': 6.2.2
'@push.rocks/smartchok': 1.1.1 '@push.rocks/smartchok': 1.1.1
@@ -4511,7 +4511,7 @@ snapshots:
'@borewit/text-codec@0.1.1': {} '@borewit/text-codec@0.1.1': {}
'@cloudflare/workers-types@4.20250813.0': {} '@cloudflare/workers-types@4.20250816.0': {}
'@colors/colors@1.6.0': {} '@colors/colors@1.6.0': {}
@@ -4543,7 +4543,7 @@ snapshots:
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrouter': 1.3.3 '@push.rocks/smartrouter': 1.3.3
'@push.rocks/smartrx': 3.0.10 '@push.rocks/smartrx': 3.0.10
'@push.rocks/smartstate': 2.0.25 '@push.rocks/smartstate': 2.0.26
'@push.rocks/smartstring': 4.0.15 '@push.rocks/smartstring': 4.0.15
'@push.rocks/smarturl': 3.1.0 '@push.rocks/smarturl': 3.1.0
'@push.rocks/webrequest': 3.0.37 '@push.rocks/webrequest': 3.0.37
@@ -4709,14 +4709,14 @@ snapshots:
'@push.rocks/smartnpm': 2.0.4 '@push.rocks/smartnpm': 2.0.4
'@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpath': 6.0.0
'@push.rocks/smartrequest': 4.2.1 '@push.rocks/smartrequest': 4.2.1
'@push.rocks/smartshell': 3.2.3 '@push.rocks/smartshell': 3.2.4
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@git.zone/tsrun@1.3.3': '@git.zone/tsrun@1.3.3':
dependencies: dependencies:
'@push.rocks/smartfile': 11.2.5 '@push.rocks/smartfile': 11.2.5
'@push.rocks/smartshell': 3.2.3 '@push.rocks/smartshell': 3.2.4
tsx: 4.20.4 tsx: 4.20.4
'@git.zone/tstest@2.3.2(@aws-sdk/credential-providers@3.864.0)(socks@2.8.7)(typescript@5.8.3)': '@git.zone/tstest@2.3.2(@aws-sdk/credential-providers@3.864.0)(socks@2.8.7)(typescript@5.8.3)':
@@ -4725,7 +4725,7 @@ snapshots:
'@git.zone/tsbundle': 2.5.1 '@git.zone/tsbundle': 2.5.1
'@git.zone/tsrun': 1.3.3 '@git.zone/tsrun': 1.3.3
'@push.rocks/consolecolor': 2.0.3 '@push.rocks/consolecolor': 2.0.3
'@push.rocks/qenv': 6.1.2 '@push.rocks/qenv': 6.1.3
'@push.rocks/smartbrowser': 2.0.8(typescript@5.8.3) '@push.rocks/smartbrowser': 2.0.8(typescript@5.8.3)
'@push.rocks/smartchok': 1.1.1 '@push.rocks/smartchok': 1.1.1
'@push.rocks/smartcrypto': 2.0.4 '@push.rocks/smartcrypto': 2.0.4
@@ -4740,7 +4740,7 @@ snapshots:
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 2.1.0 '@push.rocks/smartrequest': 2.1.0
'@push.rocks/smarts3': 2.2.5 '@push.rocks/smarts3': 2.2.5
'@push.rocks/smartshell': 3.2.3 '@push.rocks/smartshell': 3.2.4
'@push.rocks/smarttime': 4.1.1 '@push.rocks/smarttime': 4.1.1
'@types/ws': 8.18.1 '@types/ws': 8.18.1
figures: 6.1.0 figures: 6.1.0
@@ -4953,7 +4953,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
'@push.rocks/qenv@6.1.2': '@push.rocks/qenv@6.1.3':
dependencies: dependencies:
'@api.global/typedrequest': 3.1.10 '@api.global/typedrequest': 3.1.10
'@configvault.io/interfaces': 1.0.17 '@configvault.io/interfaces': 1.0.17
@@ -5319,7 +5319,7 @@ snapshots:
'@push.rocks/smartpuppeteer@2.0.5(typescript@5.8.3)': '@push.rocks/smartpuppeteer@2.0.5(typescript@5.8.3)':
dependencies: dependencies:
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartshell': 3.2.3 '@push.rocks/smartshell': 3.2.4
puppeteer: 24.16.2(typescript@5.8.3) puppeteer: 24.16.2(typescript@5.8.3)
tree-kill: 1.2.2 tree-kill: 1.2.2
transitivePeerDependencies: transitivePeerDependencies:
@@ -5368,7 +5368,7 @@ snapshots:
- aws-crt - aws-crt
- supports-color - supports-color
'@push.rocks/smartshell@3.2.3': '@push.rocks/smartshell@3.2.4':
dependencies: dependencies:
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartexit': 1.0.23 '@push.rocks/smartexit': 1.0.23
@@ -5420,7 +5420,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@push.rocks/smartstate@2.0.25': '@push.rocks/smartstate@2.0.26':
dependencies: dependencies:
'@push.rocks/lik': 6.2.2 '@push.rocks/lik': 6.2.2
'@push.rocks/smarthash': 3.2.3 '@push.rocks/smarthash': 3.2.3

85
test/test.redaction.ts Normal file
View File

@@ -0,0 +1,85 @@
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as npmextra from '../ts/index.js';
// Test that sensitive values are properly redacted in logs
tap.test('should redact sensitive values in console output', async () => {
// Capture console.log output
const originalLog = console.log;
const logOutput: string[] = [];
console.log = (...args: any[]) => {
logOutput.push(args.join(' '));
};
try {
// Set up environment variables with sensitive data
process.env['API_KEY'] = 'super-secret-api-key-12345';
process.env['DATABASE_PASSWORD'] = 'myP@ssw0rd123';
process.env['AUTH_TOKEN'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ';
process.env['PUBLIC_URL'] = 'https://example.com';
process.env['DEBUG_MODE'] = 'true';
// Create AppData with sensitive environment mappings
const appData = await npmextra.AppData.createAndInit({
ephemeral: true,
envMapping: {
apiKey: 'API_KEY',
dbPassword: 'DATABASE_PASSWORD',
authToken: 'AUTH_TOKEN',
publicUrl: 'PUBLIC_URL',
debugMode: 'boolean:DEBUG_MODE',
nestedConfig: {
secretKey: 'API_KEY',
nonSecret: 'PUBLIC_URL'
}
}
});
// Restore console.log
console.log = originalLog;
// Check that sensitive values were redacted in logs
const combinedOutput = logOutput.join('\n');
// API_KEY should be redacted
expect(combinedOutput).toContain('sup...[26 chars]');
expect(combinedOutput).not.toContain('super-secret-api-key-12345');
// DATABASE_PASSWORD should be redacted
expect(combinedOutput).toContain('myP...[13 chars]');
expect(combinedOutput).not.toContain('myP@ssw0rd123');
// AUTH_TOKEN should be redacted (JWT tokens starting with eyJ)
expect(combinedOutput).toContain('eyJ...[');
expect(combinedOutput).not.toContain('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');
// PUBLIC_URL should not be redacted (not sensitive)
expect(combinedOutput).toContain('https://example.com');
// DEBUG_MODE should not be redacted (not sensitive)
expect(combinedOutput).toContain('true');
// Verify data is still stored correctly (not redacted in actual storage)
const kvStore = await appData.getKvStore();
const apiKey = await kvStore.readKey('apiKey');
const dbPassword = await kvStore.readKey('dbPassword');
const publicUrl = await kvStore.readKey('publicUrl');
// Actual values should be stored correctly
expect(apiKey).toEqual('super-secret-api-key-12345');
expect(dbPassword).toEqual('myP@ssw0rd123');
expect(publicUrl).toEqual('https://example.com');
} finally {
// Restore console.log in case of test failure
console.log = originalLog;
// Clean up environment variables
delete process.env['API_KEY'];
delete process.env['DATABASE_PASSWORD'];
delete process.env['AUTH_TOKEN'];
delete process.env['PUBLIC_URL'];
delete process.env['DEBUG_MODE'];
}
});
export default tap.start();

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/npmextra', name: '@push.rocks/npmextra',
version: '5.3.1', version: '5.3.3',
description: 'A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.' description: 'A utility to enhance npm with additional configuration, tool management capabilities, and a key-value store for project setups.'
} }

View File

@@ -16,6 +16,48 @@ function getQenv(): plugins.qenv.Qenv {
return sharedQenv; return sharedQenv;
} }
// ============================================================================
// Security - Redaction for sensitive data
// ============================================================================
/**
* Redacts sensitive values in logs to prevent exposure of secrets
*/
function redactSensitiveValue(key: string, value: unknown): string {
// List of patterns that indicate sensitive data
const sensitivePatterns = [
/secret/i, /token/i, /key/i, /password/i, /pass/i,
/api/i, /credential/i, /auth/i, /private/i, /jwt/i,
/cert/i, /signature/i, /bearer/i
];
// Check if key contains sensitive pattern
const isSensitive = sensitivePatterns.some(pattern => pattern.test(key));
if (isSensitive) {
if (typeof value === 'string') {
// Show first 3 chars and length for debugging
return value.length > 3
? `${value.substring(0, 3)}...[${value.length} chars]`
: '[redacted]';
}
return '[redacted]';
}
// Check if value looks like a JWT token or base64 secret
if (typeof value === 'string') {
// JWT tokens start with eyJ
if (value.startsWith('eyJ')) {
return `eyJ...[${value.length} chars]`;
}
// Very long strings might be encoded secrets
if (value.length > 100) {
return `${value.substring(0, 50)}...[${value.length} chars total]`;
}
}
return JSON.stringify(value);
}
// ============================================================================ // ============================================================================
// Type Converters - Centralized conversion logic // Type Converters - Centralized conversion logic
// ============================================================================ // ============================================================================
@@ -210,12 +252,14 @@ function applyTransforms(value: unknown, transforms: Transform[]): unknown {
*/ */
async function processMappingValue(mappingString: string): Promise<unknown> { async function processMappingValue(mappingString: string): Promise<unknown> {
const spec = parseMappingSpec(mappingString); const spec = parseMappingSpec(mappingString);
const keyName = spec.source.type === 'env' ? spec.source.key : 'hardcoded';
console.log(` 🔍 Processing mapping: "${mappingString}"`); console.log(` 🔍 Processing mapping: "${mappingString}"`);
console.log(` Source: ${spec.source.type === 'env' ? `env:${spec.source.key}` : `hard:${spec.source.value}`}`); console.log(` Source: ${spec.source.type === 'env' ? `env:${spec.source.key}` : `hard:${spec.source.value}`}`);
console.log(` Transforms: ${spec.transforms.length > 0 ? spec.transforms.join(', ') : 'none'}`); console.log(` Transforms: ${spec.transforms.length > 0 ? spec.transforms.join(', ') : 'none'}`);
const rawValue = await resolveSource(spec.source); const rawValue = await resolveSource(spec.source);
console.log(` Raw value: ${JSON.stringify(rawValue)} (type: ${typeof rawValue})`); console.log(` Raw value: ${redactSensitiveValue(keyName, rawValue)} (type: ${typeof rawValue})`);
if (rawValue === undefined || rawValue === null) { if (rawValue === undefined || rawValue === null) {
console.log(` ⚠️ Raw value is undefined/null, returning undefined`); console.log(` ⚠️ Raw value is undefined/null, returning undefined`);
@@ -223,7 +267,7 @@ async function processMappingValue(mappingString: string): Promise<unknown> {
} }
const result = applyTransforms(rawValue, spec.transforms); const result = applyTransforms(rawValue, spec.transforms);
console.log(` Final value: ${JSON.stringify(result)} (type: ${typeof result})`); console.log(` Final value: ${redactSensitiveValue(keyName, result)} (type: ${typeof result})`);
return result; return result;
} }
@@ -253,7 +297,7 @@ async function evaluateMappingValue(mappingValue: any): Promise<any> {
// Only skip if explicitly undefined // Only skip if explicitly undefined
if (evaluated !== undefined) { if (evaluated !== undefined) {
result[key] = evaluated; result[key] = evaluated;
console.log(` ✓ Nested key "${key}" = ${JSON.stringify(evaluated)} (type: ${typeof evaluated})`); console.log(` ✓ Nested key "${key}" = ${redactSensitiveValue(key, evaluated)} (type: ${typeof evaluated})`);
} else { } else {
console.log(` ⚠️ Nested key "${key}" evaluated to undefined, skipping`); console.log(` ⚠️ Nested key "${key}" evaluated to undefined, skipping`);
} }
@@ -262,7 +306,8 @@ async function evaluateMappingValue(mappingValue: any): Promise<any> {
} }
// For any other type (numbers, booleans, etc.), return as-is // For any other type (numbers, booleans, etc.), return as-is
console.log(` 📎 Returning value as-is: ${JSON.stringify(mappingValue)} (type: ${typeof mappingValue})`); // Note: We don't have key context here, so we'll just indicate the type
console.log(` 📎 Returning value as-is: [value] (type: ${typeof mappingValue})`);
return mappingValue; return mappingValue;
} }
@@ -434,7 +479,7 @@ export class AppData<T = any> {
const valuePreview = evaluated === null ? 'null' : const valuePreview = evaluated === null ? 'null' :
typeof evaluated === 'object' ? typeof evaluated === 'object' ?
(Array.isArray(evaluated) ? `[${evaluated.length} items]` : `{${Object.keys(evaluated).length} keys}`) : (Array.isArray(evaluated) ? `[${evaluated.length} items]` : `{${Object.keys(evaluated).length} keys}`) :
JSON.stringify(evaluated); redactSensitiveValue(key, evaluated);
console.log(` ✅ Successfully processed key "${key}" = ${valuePreview} (type: ${valueType})`); console.log(` ✅ Successfully processed key "${key}" = ${valuePreview} (type: ${valueType})`);
} else { } else {
console.log(` ⚠️ Key "${key}" evaluated to undefined, skipping`); console.log(` ⚠️ Key "${key}" evaluated to undefined, skipping`);