Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
fdc2420238 | |||
e3a76ca577 | |||
2cc0da4462 | |||
b1e4ab09db |
15
changelog.md
15
changelog.md
@@ -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.
|
||||||
|
|
||||||
|
@@ -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
42
pnpm-lock.yaml
generated
@@ -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
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.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.'
|
||||||
}
|
}
|
||||||
|
@@ -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