mirror of
https://github.com/Keeper-Security/harness-integration.git
synced 2026-06-04 10:14:56 +08:00
harness CI drone plugin PLUGIN_KSM_CONFIG convention added, multi-platform Docker (amd64/arm64) support and added MIT license
This commit is contained in:
@@ -101,10 +101,11 @@ jobs:
|
||||
echo "Image tag: ${IMAGE_TAG}"
|
||||
echo "Latest tag: ${LATEST_TAG}"
|
||||
|
||||
- name: Build Docker image
|
||||
- name: Build Docker image (multi-platform)
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/') }}
|
||||
tags: |
|
||||
${{ steps.docker_tags.outputs.IMAGE_TAG }}
|
||||
@@ -281,11 +282,10 @@ jobs:
|
||||
spec:
|
||||
image: ${{ steps.docker_image.outputs.IMAGE_NAME }}
|
||||
settings:
|
||||
ksm_config: <+secrets.getValue("Keeper_Config_Secret")>
|
||||
secrets: |
|
||||
VeYTRo-PHElAwfQT6f0TIA/field/password > DB_PASSWORD
|
||||
VeYTRo-PHElAwfQT6f0TIA/field/login > DB_USERNAME
|
||||
envVariables:
|
||||
KSM_CONFIG: <+secrets.getValue("Keeper_Config_Secret")>
|
||||
```
|
||||
|
||||
### What's Changed
|
||||
@@ -293,4 +293,4 @@ jobs:
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -59,13 +59,12 @@ pipeline:
|
||||
name: Fetch_Keeper_Secrets
|
||||
identifier: Fetch_Keeper_Secrets
|
||||
spec:
|
||||
image: dhborse/keeper-harness-plugin
|
||||
image: keeper/harness-plugin:latest
|
||||
settings:
|
||||
ksm_config: <+secrets.getValue("keeper_base64_secret")>
|
||||
secrets: |
|
||||
RECORD_UID/field/password > PASSWORD
|
||||
RECORD_UID/field/login > USERNAME
|
||||
envVariables:
|
||||
KSM_CONFIG: <+secrets.getValue("keeper_base64_secret")>
|
||||
- step:
|
||||
type: Run
|
||||
name: Use_Secrets
|
||||
@@ -87,13 +86,15 @@ pipeline:
|
||||
|
||||
## Inputs
|
||||
|
||||
### KSM_CONFIG
|
||||
### ksm_config (PLUGIN_KSM_CONFIG)
|
||||
|
||||
Keeper Secrets Manager configuration for authentication. Store in Harness secrets and reference:
|
||||
Keeper Secrets Manager configuration for authentication. Store in Harness secrets and reference via the standard `settings` block (Harness/Drone maps `ksm_config` to `PLUGIN_KSM_CONFIG`):
|
||||
|
||||
```yaml
|
||||
envVariables:
|
||||
KSM_CONFIG: <+secrets.getValue("Keeper_Config_Secret")>
|
||||
settings:
|
||||
ksm_config: <+secrets.getValue("Keeper_Config_Secret")>
|
||||
secrets: |
|
||||
RECORD_UID/field/password > PASSWORD
|
||||
```
|
||||
|
||||
**Supported Formats:**
|
||||
@@ -172,7 +173,7 @@ Secrets are stored in `/harness/secrets/` directory. Read them in subsequent ste
|
||||
- **Scope:** Pipeline execution only - automatically cleaned up after completion
|
||||
- **Access:** Read files directly using `cat` or file operations
|
||||
|
||||
The `envVariables` section is **only** used to pass `KSM_CONFIG` to the plugin. Actual secrets are written to files in `/harness/secrets/` directory.
|
||||
The `ksm_config` setting is passed via the standard `settings` block (mapped to `PLUGIN_KSM_CONFIG`). Actual secrets are written to files in `/harness/secrets/` directory.
|
||||
|
||||
## Security
|
||||
|
||||
@@ -194,7 +195,7 @@ The `envVariables` section is **only** used to pass `KSM_CONFIG` to the plugin.
|
||||
|
||||
| Error | Solution |
|
||||
|-------|----------|
|
||||
| `KSM config is required` | Verify secret exists and expression `<+secrets.getValue("SECRET_NAME")>` is correct |
|
||||
| `KSM config is required` | Verify secret exists, `ksm_config` is set in settings, and expression `<+secrets.getValue("SECRET_NAME")>` is correct |
|
||||
| `Invalid token format` | Check token starts with `US:` or `{` for JSON config |
|
||||
| `Value not found for notation` | Verify Record UID, field name (case-sensitive), and Application permissions |
|
||||
| `Failed to download file` | Check file exists in record, name matches exactly, and Application has file access permissions |
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
"name": "harness_keeper_plugin",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"description": "Keeper Secrets Manager integration for Harness CI",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "jest --coverage",
|
||||
@@ -11,8 +11,8 @@
|
||||
"lint:fix": "eslint . --fix"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"author": "Keeper Security",
|
||||
"license": "MIT",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@keeper-security/secrets-manager-core": "^17.4.0"
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ const splitInput = (text) => {
|
||||
const processToken = (rawToken) => {
|
||||
if (!rawToken) {
|
||||
core.error('KSM config is required');
|
||||
core.error('Set KSM_CONFIG environment variable');
|
||||
core.error('Set PLUGIN_KSM_CONFIG (or ksm_config in settings)');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ const runPlugin = async () => {
|
||||
core.info('Starting Keeper Secrets Manager plugin');
|
||||
fs.mkdirSync('/app', { recursive: true });
|
||||
|
||||
const { token, isConfigJson, config } = processToken(process.env.KSM_CONFIG);
|
||||
const { token, isConfigJson, config } = processToken(process.env.PLUGIN_KSM_CONFIG);
|
||||
const inputs = parseSecretMappings();
|
||||
const storage = await setupStorage(token, isConfigJson, config);
|
||||
const secrets = await getSecrets({ storage });
|
||||
|
||||
+65
-65
@@ -72,7 +72,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
pathModule.join = path.join;
|
||||
|
||||
// Clear environment variables
|
||||
delete process.env.KSM_CONFIG;
|
||||
delete process.env.PLUGIN_KSM_CONFIG;
|
||||
delete process.env.PLUGIN_SECRETS;
|
||||
});
|
||||
|
||||
@@ -89,45 +89,45 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('processToken - Error Cases', () => {
|
||||
test('should exit when rawToken is null', () => {
|
||||
delete process.env.KSM_CONFIG;
|
||||
delete process.env.PLUGIN_KSM_CONFIG;
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
expect(mockStderrWrite).toHaveBeenCalledWith(expect.stringContaining('KSM config is required'));
|
||||
});
|
||||
|
||||
test('should exit when rawToken is undefined', () => {
|
||||
delete process.env.KSM_CONFIG;
|
||||
delete process.env.PLUGIN_KSM_CONFIG;
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when rawToken is empty string', () => {
|
||||
process.env.KSM_CONFIG = '';
|
||||
process.env.PLUGIN_KSM_CONFIG = '';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when token contains ${ Harness expression', () => {
|
||||
process.env.KSM_CONFIG = '${harness.expression}';
|
||||
process.env.PLUGIN_KSM_CONFIG = '${harness.expression}';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
expect(mockStderrWrite).toHaveBeenCalledWith(expect.stringContaining('Harness expression not resolved'));
|
||||
});
|
||||
|
||||
test('should exit when token contains <+ Harness expression', () => {
|
||||
process.env.KSM_CONFIG = '<+expression>';
|
||||
process.env.PLUGIN_KSM_CONFIG = '<+expression>';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when token contains ngSecretManager', () => {
|
||||
process.env.KSM_CONFIG = 'ngSecretManager.token';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'ngSecretManager.token';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when token is empty after trim', () => {
|
||||
process.env.KSM_CONFIG = ' ';
|
||||
process.env.PLUGIN_KSM_CONFIG = ' ';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
expect(mockStderrWrite).toHaveBeenCalledWith(expect.stringContaining('Token is null or empty'));
|
||||
@@ -135,7 +135,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
test('should exit when JSON config is missing hostname field', () => {
|
||||
const jsonConfig = JSON.stringify({ clientId: 'client', privateKey: 'key' });
|
||||
process.env.KSM_CONFIG = jsonConfig;
|
||||
process.env.PLUGIN_KSM_CONFIG = jsonConfig;
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
expect(mockStderrWrite).toHaveBeenCalledWith(expect.stringContaining('Missing required fields'));
|
||||
@@ -143,34 +143,34 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
test('should exit when JSON config is missing clientId field', () => {
|
||||
const jsonConfig = JSON.stringify({ hostname: 'test.com', privateKey: 'key' });
|
||||
process.env.KSM_CONFIG = jsonConfig;
|
||||
process.env.PLUGIN_KSM_CONFIG = jsonConfig;
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when JSON config is missing privateKey field', () => {
|
||||
const jsonConfig = JSON.stringify({ hostname: 'test.com', clientId: 'client' });
|
||||
process.env.KSM_CONFIG = jsonConfig;
|
||||
process.env.PLUGIN_KSM_CONFIG = jsonConfig;
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when JSON config is missing multiple fields', () => {
|
||||
const jsonConfig = JSON.stringify({ hostname: 'test.com' });
|
||||
process.env.KSM_CONFIG = jsonConfig;
|
||||
process.env.PLUGIN_KSM_CONFIG = jsonConfig;
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
test('should exit when JSON config is invalid', () => {
|
||||
process.env.KSM_CONFIG = '{invalid json}';
|
||||
process.env.PLUGIN_KSM_CONFIG = '{invalid json}';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
expect(mockStderrWrite).toHaveBeenCalledWith(expect.stringContaining('Invalid JSON config'));
|
||||
});
|
||||
|
||||
test('should exit when token format is invalid (not US: or JSON)', () => {
|
||||
process.env.KSM_CONFIG = 'INVALID:token';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'INVALID:token';
|
||||
require('../index');
|
||||
expect(mockExit).toHaveBeenCalledWith(1);
|
||||
expect(mockStderrWrite).toHaveBeenCalledWith(expect.stringContaining('Invalid token format'));
|
||||
@@ -180,7 +180,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
describe('processToken - Success Cases', () => {
|
||||
test('should decode base64 token successfully', async () => {
|
||||
const base64Token = Buffer.from('US:decoded-token').toString('base64');
|
||||
process.env.KSM_CONFIG = base64Token;
|
||||
process.env.PLUGIN_KSM_CONFIG = base64Token;
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -193,7 +193,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle base64 decode error gracefully', async () => {
|
||||
process.env.KSM_CONFIG = 'invalid-base64!@#';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'invalid-base64!@#';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -207,7 +207,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
test('should parse valid JSON config', async () => {
|
||||
const jsonConfig = JSON.stringify({ hostname: 'test.com', clientId: 'client', privateKey: 'key' });
|
||||
process.env.KSM_CONFIG = jsonConfig;
|
||||
process.env.PLUGIN_KSM_CONFIG = jsonConfig;
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
const mockStorage = {};
|
||||
localConfigStorage.mockReturnValue(mockStorage);
|
||||
@@ -221,7 +221,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should accept US: token format', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test-token';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test-token';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
const mockStorage = {};
|
||||
localConfigStorage.mockReturnValue(mockStorage);
|
||||
@@ -235,7 +235,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle token with whitespace', async () => {
|
||||
process.env.KSM_CONFIG = ' US:test-token ';
|
||||
process.env.PLUGIN_KSM_CONFIG = ' US:test-token ';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -248,7 +248,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should call info function on plugin start', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -268,7 +268,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
throw new Error('Base64 decode error');
|
||||
});
|
||||
|
||||
process.env.KSM_CONFIG = 'not-us-format-token';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'not-us-format-token';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -284,7 +284,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('parseSecretMappings', () => {
|
||||
test('should treat env: prefix as literal destination name (no special parsing)', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>env:VAR_NAME';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -297,7 +297,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should parse file: destination type', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -310,7 +310,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should parse output destination type (default)', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -323,7 +323,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle input without > separator', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -336,7 +336,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle multiple > characters', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>part1>destination';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -348,7 +348,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle multiple secret mappings', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation1>env:VAR1\nnotation2>file:/path/file\nnotation3>output1';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -361,7 +361,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should filter empty lines from multiline input', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation1>output1\n\nnotation2>output2\n \nnotation3>output3';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -374,7 +374,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle empty secrets input', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = '';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -388,7 +388,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('runPlugin - File Handling', () => {
|
||||
test('should create /app directory', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
const mockStorage = {};
|
||||
localConfigStorage.mockReturnValue(mockStorage);
|
||||
@@ -406,7 +406,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should treat file: prefix as literal destination (writes to /harness/secrets/)', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -419,7 +419,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should create /harness/secrets for any destination', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -433,7 +433,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file secret with fileId', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -448,7 +448,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file secret with fileUid', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -463,7 +463,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file secret with url', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -478,7 +478,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file secret with /file/ notation', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'record/field/file/file.txt>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -495,7 +495,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle downloadFile error gracefully', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -510,7 +510,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle Uint8Array file data', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -526,7 +526,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle Buffer file data', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -541,7 +541,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle other file data types', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -557,7 +557,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file data that is neither Uint8Array nor Buffer (line 135 branch)', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -576,7 +576,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file data that is Buffer (line 134 branch)', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -598,7 +598,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
test('should handle file data that is plain Uint8Array (not Buffer)', async () => {
|
||||
// Test the second branch: Buffer.isBuffer is false but instanceof Uint8Array is true
|
||||
// Use a plain Uint8Array (not a Buffer) to test the middle branch of the ternary
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>file:/path/to/file.txt';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -624,7 +624,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('runPlugin - Secret Value Handling', () => {
|
||||
test('should handle string secret value', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -637,7 +637,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle Buffer secret value', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -650,7 +650,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle non-string, non-buffer secret value', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -663,7 +663,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should skip secret when value is not found', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -677,7 +677,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should skip secret when value is undefined', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -692,7 +692,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('runPlugin - Destination Types', () => {
|
||||
test('should handle env-prefixed destination as literal (writes to /harness/secrets/)', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>env:VAR_NAME';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -707,7 +707,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle env-prefixed destination with Buffer data', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>env:VAR_NAME';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -722,7 +722,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle env-prefixed destination with non-Buffer data', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>env:VAR_NAME';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -738,7 +738,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
|
||||
test('should create /harness/secrets directory for non-file destinations', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -751,7 +751,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should set file permissions to 0o600 for secret files', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -764,7 +764,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should write only to /harness/secrets for output destinations', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output_var';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -779,7 +779,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('runPlugin - Error Handling', () => {
|
||||
test('should handle storage initialization error', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockRejectedValue(new Error('Storage initialization failed'));
|
||||
@@ -791,7 +791,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle token-related error with specific message', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockRejectedValue(new Error('Invalid token'));
|
||||
@@ -802,7 +802,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle expired token error', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockRejectedValue(new Error('Token expired'));
|
||||
@@ -813,7 +813,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle invalid token error', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockRejectedValue(new Error('invalid'));
|
||||
@@ -824,7 +824,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle getSecrets error', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -837,7 +837,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle generic error without token message', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -852,7 +852,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
|
||||
describe('Edge Cases and Integration', () => {
|
||||
test('should handle null secret object', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -865,7 +865,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle object secret without file properties', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -880,7 +880,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
test('should handle object secret without file properties and notation without /file/', async () => {
|
||||
// Test the branch where typeof secret === 'object' && secret !== null is true
|
||||
// but (secret.fileId || secret.fileUid || secret.url || notationIsFile) is false
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'regular/notation>output';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -896,7 +896,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle complex multiline input with mixed types', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'notation1>env:VAR1\nnotation2>file:/path/file\nnotation3>output1\nnotation4>env:VAR2';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
@@ -909,7 +909,7 @@ describe('index.js - Complete Test Suite', () => {
|
||||
});
|
||||
|
||||
test('should handle file notation with multiple file references', async () => {
|
||||
process.env.KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_KSM_CONFIG = 'US:test';
|
||||
process.env.PLUGIN_SECRETS = 'record1/field/file/file1.txt>file:/path/file1\nrecord2/field/file/file2.txt>file:/path/file2';
|
||||
localConfigStorage.mockReturnValue({});
|
||||
initializeStorage.mockResolvedValue();
|
||||
|
||||
Reference in New Issue
Block a user