fix(appdata): Redact sensitive values in AppData logs and add redaction tests
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
# 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)
|
## 2025-08-16 - 5.3.2 - fix(dependencies)
|
||||||
Bump @push.rocks/qenv to ^6.1.3 and add local Claude settings
|
Bump @push.rocks/qenv to ^6.1.3 and add local Claude settings
|
||||||
|
|
||||||
|
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
@@ -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==}
|
||||||
@@ -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)':
|
||||||
@@ -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
|
||||||
@@ -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
85
test/test.redaction.ts
Normal 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();
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/npmextra',
|
name: '@push.rocks/npmextra',
|
||||||
version: '5.3.2',
|
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.'
|
||||||
}
|
}
|
||||||
|
@@ -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`);
|
||||||
|
Reference in New Issue
Block a user