@ship.zone/szci 7.1.5 → 7.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/npm-prepare.js +138 -0
- package/bin/szci-wrapper.js +19 -2
- package/changelog.md +21 -0
- package/deno.json +1 -1
- package/package.json +2 -2
- package/readme.md +7 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/manager.npm/index.ts +38 -43
- package/ts/manager.npm/npmcredential.shared.js +111 -0
- package/ts/manager.npm/npmcredential.ts +23 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import childProcess from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
parseNpmCredentialEnvironment,
|
|
8
|
+
validateNpmRegistryUrl,
|
|
9
|
+
} from '../ts/manager.npm/npmcredential.shared.js';
|
|
10
|
+
|
|
11
|
+
const defaultRegistryUrl = 'registry.npmjs.org';
|
|
12
|
+
|
|
13
|
+
const configError = () => new Error('Invalid @ship.zone/szci npm registry configuration.');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} cwdArg
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export const readDefaultRegistryUrl = (cwdArg) => {
|
|
20
|
+
const configPath = path.join(cwdArg, '.smartconfig.json');
|
|
21
|
+
if (!fs.existsSync(configPath)) {
|
|
22
|
+
return defaultRegistryUrl;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let config;
|
|
26
|
+
try {
|
|
27
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
28
|
+
} catch {
|
|
29
|
+
throw configError();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const szciConfig = config?.['@ship.zone/szci'];
|
|
33
|
+
let configuredRegistry = defaultRegistryUrl;
|
|
34
|
+
if (szciConfig !== undefined) {
|
|
35
|
+
if (
|
|
36
|
+
!szciConfig ||
|
|
37
|
+
typeof szciConfig !== 'object' ||
|
|
38
|
+
Array.isArray(szciConfig)
|
|
39
|
+
) {
|
|
40
|
+
throw configError();
|
|
41
|
+
}
|
|
42
|
+
if (Object.prototype.hasOwnProperty.call(szciConfig, 'npmRegistryUrl')) {
|
|
43
|
+
configuredRegistry = szciConfig.npmRegistryUrl;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (typeof configuredRegistry !== 'string') {
|
|
47
|
+
throw configError();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
return validateNpmRegistryUrl(configuredRegistry);
|
|
52
|
+
} catch {
|
|
53
|
+
throw configError();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {{
|
|
59
|
+
* environment?: Record<string, string | undefined>;
|
|
60
|
+
* cwd?: string;
|
|
61
|
+
* }} [optionsArg]
|
|
62
|
+
*/
|
|
63
|
+
export const createNpmPreparePlan = (optionsArg = {}) => {
|
|
64
|
+
const environment = optionsArg.environment ?? process.env;
|
|
65
|
+
const cwd = optionsArg.cwd ?? process.cwd();
|
|
66
|
+
const npmCredentials = parseNpmCredentialEnvironment(environment);
|
|
67
|
+
if (npmCredentials.length === 0) {
|
|
68
|
+
throw new Error('No SZCI_TOKEN_NPM credentials were provided.');
|
|
69
|
+
}
|
|
70
|
+
const registryUrl = readDefaultRegistryUrl(cwd);
|
|
71
|
+
|
|
72
|
+
let npmrcFileString = '';
|
|
73
|
+
for (const npmCredential of npmCredentials) {
|
|
74
|
+
npmrcFileString += `//${npmCredential.registryUrl}/:_authToken="${npmCredential.token}"\n`;
|
|
75
|
+
}
|
|
76
|
+
npmrcFileString += `registry=https://${registryUrl}\n`;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
cwd,
|
|
80
|
+
npmCredentials,
|
|
81
|
+
npmrcFileString,
|
|
82
|
+
registryUrl,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {{
|
|
88
|
+
* environment?: Record<string, string | undefined>;
|
|
89
|
+
* cwd?: string;
|
|
90
|
+
* homeDirectory?: string;
|
|
91
|
+
* log?: (messageArg: string) => void;
|
|
92
|
+
* writeFile?: (pathArg: string, contentArg: string) => void;
|
|
93
|
+
* runNpm?: (commandArg: string, argsArg: string[]) => void;
|
|
94
|
+
* }} [optionsArg]
|
|
95
|
+
*/
|
|
96
|
+
export const runNpmPrepare = (optionsArg = {}) => {
|
|
97
|
+
const plan = createNpmPreparePlan(optionsArg);
|
|
98
|
+
const homeDirectory = optionsArg.homeDirectory ?? os.homedir();
|
|
99
|
+
const log = optionsArg.log ?? ((messageArg) => console.log(messageArg));
|
|
100
|
+
const writeFile = optionsArg.writeFile ??
|
|
101
|
+
((pathArg, contentArg) => fs.writeFileSync(pathArg, contentArg));
|
|
102
|
+
const runNpm = optionsArg.runNpm ??
|
|
103
|
+
((commandArg, argsArg) => {
|
|
104
|
+
const result = childProcess.spawnSync(commandArg, argsArg, {
|
|
105
|
+
stdio: 'inherit',
|
|
106
|
+
shell: false,
|
|
107
|
+
});
|
|
108
|
+
if (result.error) {
|
|
109
|
+
throw new Error('Failed to configure the npm cache.');
|
|
110
|
+
}
|
|
111
|
+
if (result.status !== 0) {
|
|
112
|
+
throw new Error('Failed to configure the npm cache.');
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
log('running >>npm prepare<<');
|
|
117
|
+
for (const npmCredential of plan.npmCredentials) {
|
|
118
|
+
log(`found token for ${npmCredential.registryUrl}`);
|
|
119
|
+
log(
|
|
120
|
+
npmCredential.tokenEncoding === 'plain'
|
|
121
|
+
? 'npm token not base64 encoded.'
|
|
122
|
+
: 'npm token base64 encoded.',
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
log(`setting default npm registry to ${plan.registryUrl}`);
|
|
126
|
+
log('found one or more access tokens');
|
|
127
|
+
|
|
128
|
+
writeFile(path.join(homeDirectory, '.npmrc'), plan.npmrcFileString);
|
|
129
|
+
runNpm('npm', [
|
|
130
|
+
'config',
|
|
131
|
+
'set',
|
|
132
|
+
'cache',
|
|
133
|
+
path.join(plan.cwd, '.szci_cache'),
|
|
134
|
+
'--global',
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
return plan.npmCredentials;
|
|
138
|
+
};
|
package/bin/szci-wrapper.js
CHANGED
|
@@ -145,5 +145,22 @@ function executeBinary() {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
const cliArguments = process.argv.slice(2);
|
|
149
|
+
if (
|
|
150
|
+
cliArguments.length === 2 &&
|
|
151
|
+
cliArguments[0] === 'npm' &&
|
|
152
|
+
cliArguments[1] === 'prepare'
|
|
153
|
+
) {
|
|
154
|
+
const runNodeNpmPrepare = async () => {
|
|
155
|
+
const { runNpmPrepare } = await import('./npm-prepare.js');
|
|
156
|
+
runNpmPrepare();
|
|
157
|
+
};
|
|
158
|
+
runNodeNpmPrepare().catch((error) => {
|
|
159
|
+
console.error(
|
|
160
|
+
error instanceof Error ? error.message : 'SZCI npm prepare failed.',
|
|
161
|
+
);
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
});
|
|
164
|
+
} else {
|
|
165
|
+
executeBinary();
|
|
166
|
+
}
|
package/changelog.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-07-28 - 7.1.7
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- run `szci npm prepare` through the Node wrapper without starting a platform binary
|
|
8
|
+
- Keep npm credential validation shared between the Node and Deno paths.
|
|
9
|
+
- Allow cross-platform Docker builds to prepare npm authentication when Deno cannot execute under CPU emulation.
|
|
10
|
+
- Validate the configured default registry before logging or writing `.npmrc`.
|
|
11
|
+
- Require Node 14.13.1 or newer for the Node-native prepare path.
|
|
12
|
+
|
|
13
|
+
## 2026-07-28 - 7.1.6
|
|
14
|
+
|
|
15
|
+
### Fixes
|
|
16
|
+
|
|
17
|
+
- validate npm credentials before publishing (npm)
|
|
18
|
+
- Validate all SZCI_TOKEN_NPM values before logging or writing .npmrc
|
|
19
|
+
- Reject malformed registry, encoding, or token values without exposing credential input
|
|
20
|
+
- Await credential preparation and reuse the validated registry list during npm publish
|
|
21
|
+
- remove duplicate pending npm credential fix entries (changelog)
|
|
22
|
+
- Consolidate repeated pending fix bullets under the existing npm credential validation entry.
|
|
23
|
+
|
|
3
24
|
## 2026-07-28 - 7.1.5
|
|
4
25
|
|
|
5
26
|
### Fixes
|
package/deno.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ship.zone/szci",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.7",
|
|
4
4
|
"description": "Serve Zone CI - A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities. Powered by Deno with standalone executables.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Node.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"changelog.md"
|
|
46
46
|
],
|
|
47
47
|
"engines": {
|
|
48
|
-
"node": ">=14.
|
|
48
|
+
"node": ">=14.13.1"
|
|
49
49
|
},
|
|
50
50
|
"os": [
|
|
51
51
|
"darwin",
|
package/readme.md
CHANGED
|
@@ -118,6 +118,10 @@ szci npm prepare # Generates ~/.npmrc from SZCI_TOKEN_NPM* env vars
|
|
|
118
118
|
szci npm publish # Full workflow: prepare → install → build → clean → npm publish
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
`szci npm prepare` runs directly through the Node wrapper, so npm authentication
|
|
122
|
+
setup remains available during cross-platform Docker builds where a Deno binary
|
|
123
|
+
cannot execute under CPU emulation.
|
|
124
|
+
|
|
121
125
|
The `publish` command supports multi-registry publishing. If `npmAccessLevel` is `public` and a Verdaccio registry is configured, it publishes to both npm and Verdaccio automatically.
|
|
122
126
|
|
|
123
127
|
### `szci node` — Node.js Version Management
|
|
@@ -199,6 +203,9 @@ SZCI_TOKEN_NPM_1="registry.npmjs.org|dGhlLXRva2VuLWhlcmU="
|
|
|
199
203
|
SZCI_TOKEN_NPM_2="verdaccio.example.com|the-token-here|plain"
|
|
200
204
|
```
|
|
201
205
|
|
|
206
|
+
SZCI validates every configured npm credential before writing `.npmrc`. Malformed
|
|
207
|
+
registry, encoding, or token values fail without logging the credential.
|
|
208
|
+
|
|
202
209
|
### SSH Keys
|
|
203
210
|
|
|
204
211
|
Pipe-delimited format: `host|privKeyBase64|pubKeyBase64`
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@ship.zone/szci',
|
|
6
|
-
version: '7.1.
|
|
6
|
+
version: '7.1.7',
|
|
7
7
|
description: 'Serve Zone CI - A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities. Powered by Deno with standalone executables.'
|
|
8
8
|
}
|
package/ts/manager.npm/index.ts
CHANGED
|
@@ -4,6 +4,11 @@ import * as paths from '../szci.paths.ts';
|
|
|
4
4
|
import { logger } from '../szci.logging.ts';
|
|
5
5
|
import { bash, bashNoError, nvmAvailable } from '../szci.bash.ts';
|
|
6
6
|
import { Szci } from '../szci.classes.szci.ts';
|
|
7
|
+
import {
|
|
8
|
+
type INpmCredential,
|
|
9
|
+
parseNpmCredentialEnvironment,
|
|
10
|
+
validateNpmRegistryUrl,
|
|
11
|
+
} from './npmcredential.ts';
|
|
7
12
|
|
|
8
13
|
export class SzciNpmManager {
|
|
9
14
|
public szciRef: Szci;
|
|
@@ -42,7 +47,7 @@ export class SzciNpmManager {
|
|
|
42
47
|
} else {
|
|
43
48
|
logger.log(
|
|
44
49
|
'info',
|
|
45
|
-
`>>szci npm ...<< cli arguments invalid... Please read the documentation
|
|
50
|
+
`>>szci npm ...<< cli arguments invalid... Please read the documentation.`,
|
|
46
51
|
);
|
|
47
52
|
Deno.exit(1);
|
|
48
53
|
}
|
|
@@ -51,66 +56,55 @@ export class SzciNpmManager {
|
|
|
51
56
|
/**
|
|
52
57
|
* authenticates npm with token from env var
|
|
53
58
|
*/
|
|
54
|
-
public async prepare() {
|
|
59
|
+
public async prepare(): Promise<INpmCredential[]> {
|
|
55
60
|
logger.log('info', 'running >>npm prepare<<');
|
|
56
61
|
const config = this.szciRef.szciConfig.getConfig();
|
|
57
|
-
|
|
58
|
-
await plugins.smartobject.forEachMinimatch(
|
|
62
|
+
const npmCredentials = parseNpmCredentialEnvironment(
|
|
59
63
|
Deno.env.toObject(),
|
|
60
|
-
'SZCI_TOKEN_NPM*',
|
|
61
|
-
(npmEnvArg: string) => {
|
|
62
|
-
if (!npmEnvArg) {
|
|
63
|
-
logger.log('note','found empty token...');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const npmRegistryUrl = npmEnvArg.split('|')[0];
|
|
67
|
-
logger.log('ok', `found token for ${npmRegistryUrl}`);
|
|
68
|
-
let npmToken = npmEnvArg.split('|')[1];
|
|
69
|
-
if (npmEnvArg.split('|')[2] && npmEnvArg.split('|')[2] === 'plain') {
|
|
70
|
-
logger.log('ok', 'npm token not base64 encoded.');
|
|
71
|
-
} else {
|
|
72
|
-
logger.log('ok', 'npm token base64 encoded.');
|
|
73
|
-
npmToken = plugins.smartstring.base64.decode(npmToken);
|
|
74
|
-
}
|
|
75
|
-
npmrcFileString += `//${npmRegistryUrl}/:_authToken="${npmToken}"\n`;
|
|
76
|
-
}
|
|
77
64
|
);
|
|
78
|
-
|
|
79
|
-
|
|
65
|
+
if (npmCredentials.length === 0) {
|
|
66
|
+
throw new Error('No SZCI_TOKEN_NPM credentials were provided.');
|
|
67
|
+
}
|
|
68
|
+
const defaultRegistryUrl = validateNpmRegistryUrl(config.npmRegistryUrl);
|
|
80
69
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
logger.log('
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
let npmrcFileString: string = '';
|
|
71
|
+
for (const npmCredential of npmCredentials) {
|
|
72
|
+
logger.log('ok', `found token for ${npmCredential.registryUrl}`);
|
|
73
|
+
logger.log(
|
|
74
|
+
'ok',
|
|
75
|
+
npmCredential.tokenEncoding === 'plain'
|
|
76
|
+
? 'npm token not base64 encoded.'
|
|
77
|
+
: 'npm token base64 encoded.',
|
|
78
|
+
);
|
|
79
|
+
npmrcFileString += `//${npmCredential.registryUrl}/:_authToken="${npmCredential.token}"\n`;
|
|
87
80
|
}
|
|
88
81
|
|
|
82
|
+
logger.log('info', `setting default npm registry to ${defaultRegistryUrl}`);
|
|
83
|
+
npmrcFileString += `registry=https://${defaultRegistryUrl}\n`;
|
|
84
|
+
logger.log('info', 'found one or more access tokens');
|
|
85
|
+
|
|
89
86
|
// lets save it to disk
|
|
90
87
|
plugins.smartfile.memory.toFsSync(npmrcFileString, '/root/.npmrc');
|
|
91
88
|
|
|
92
89
|
// lets set the cache directory
|
|
93
90
|
await bash(`npm config set cache ${paths.SzciCacheDir} --global `);
|
|
94
91
|
|
|
95
|
-
return;
|
|
92
|
+
return npmCredentials;
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
/**
|
|
99
96
|
* publish a package to npm
|
|
100
97
|
*/
|
|
101
98
|
public async publish() {
|
|
102
|
-
const buildPublishCommand = async (
|
|
99
|
+
const buildPublishCommand = async (
|
|
100
|
+
npmCredentialsArg: INpmCredential[],
|
|
101
|
+
) => {
|
|
103
102
|
let npmAccessCliString = ``;
|
|
104
103
|
let npmRegistryCliString = ``;
|
|
105
104
|
let publishVerdaccioAsWell = false;
|
|
106
105
|
const config = this.szciRef.szciConfig.getConfig();
|
|
107
|
-
const availableRegistries
|
|
108
|
-
|
|
109
|
-
Deno.env.toObject(),
|
|
110
|
-
'SZCI_TOKEN_NPM*',
|
|
111
|
-
(npmEnvArg: string) => {
|
|
112
|
-
availableRegistries.push(npmEnvArg.split('|')[0]);
|
|
113
|
-
}
|
|
106
|
+
const availableRegistries = npmCredentialsArg.map(
|
|
107
|
+
(npmCredentialArg) => npmCredentialArg.registryUrl,
|
|
114
108
|
);
|
|
115
109
|
|
|
116
110
|
// -> configure package access level
|
|
@@ -140,13 +134,14 @@ export class SzciNpmManager {
|
|
|
140
134
|
if (verdaccioRegistry) {
|
|
141
135
|
logger.log(
|
|
142
136
|
'info',
|
|
143
|
-
`package is public and verdaccio registry is specified. Also publishing to Verdaccio
|
|
137
|
+
`package is public and verdaccio registry is specified. Also publishing to Verdaccio!`,
|
|
144
138
|
);
|
|
145
|
-
publishCommand =
|
|
139
|
+
publishCommand =
|
|
140
|
+
`${publishCommand} && npm publish ${npmAccessCliString} --registry=https://${verdaccioRegistry}`;
|
|
146
141
|
} else {
|
|
147
142
|
logger.log(
|
|
148
143
|
'error',
|
|
149
|
-
`This package should also be published to Verdaccio, however there is no Verdaccio registry data available
|
|
144
|
+
`This package should also be published to Verdaccio, however there is no Verdaccio registry data available!`,
|
|
150
145
|
);
|
|
151
146
|
}
|
|
152
147
|
}
|
|
@@ -155,7 +150,7 @@ export class SzciNpmManager {
|
|
|
155
150
|
|
|
156
151
|
// -> preparing
|
|
157
152
|
logger.log('info', `now preparing environment:`);
|
|
158
|
-
this.prepare();
|
|
153
|
+
const npmCredentials = await this.prepare();
|
|
159
154
|
await bash(`npm -v`);
|
|
160
155
|
await bash(`pnpm -v`);
|
|
161
156
|
|
|
@@ -173,7 +168,7 @@ export class SzciNpmManager {
|
|
|
173
168
|
|
|
174
169
|
// -> publish it
|
|
175
170
|
logger.log('info', `now invoking npm to publish the package!`);
|
|
176
|
-
await bash(await buildPublishCommand());
|
|
171
|
+
await bash(await buildPublishCommand(npmCredentials));
|
|
177
172
|
logger.log('success', `Package was successfully published!`);
|
|
178
173
|
}
|
|
179
174
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const invalidCredentialError = () =>
|
|
2
|
+
new Error(
|
|
3
|
+
'Invalid SZCI_TOKEN_NPM credential. Expected registry|token[|plain].',
|
|
4
|
+
);
|
|
5
|
+
|
|
6
|
+
const registryUrlRegex = /^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?(?::[0-9]{1,5})?$/;
|
|
7
|
+
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
8
|
+
const unsafeTokenRegex = /[\u0000-\u001f\u007f"\\]/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} registryUrlArg
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
export const validateNpmRegistryUrl = (registryUrlArg) => {
|
|
15
|
+
if (
|
|
16
|
+
!registryUrlArg ||
|
|
17
|
+
registryUrlArg.length > 512 ||
|
|
18
|
+
!registryUrlRegex.test(registryUrlArg)
|
|
19
|
+
) {
|
|
20
|
+
throw invalidCredentialError();
|
|
21
|
+
}
|
|
22
|
+
return registryUrlArg;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} npmEnvironmentValueArg
|
|
27
|
+
* @returns {{
|
|
28
|
+
* registryUrl: string;
|
|
29
|
+
* token: string;
|
|
30
|
+
* tokenEncoding: 'base64' | 'plain';
|
|
31
|
+
* }}
|
|
32
|
+
*/
|
|
33
|
+
export const parseNpmCredential = (npmEnvironmentValueArg) => {
|
|
34
|
+
const fields = npmEnvironmentValueArg.split('|');
|
|
35
|
+
if (fields.length < 2 || fields.length > 3) {
|
|
36
|
+
throw invalidCredentialError();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const registryUrl = validateNpmRegistryUrl(fields[0]);
|
|
40
|
+
const encodedToken = fields[1];
|
|
41
|
+
const encodingMarker = fields[2];
|
|
42
|
+
if (
|
|
43
|
+
!encodedToken ||
|
|
44
|
+
(encodingMarker !== undefined && encodingMarker !== 'plain')
|
|
45
|
+
) {
|
|
46
|
+
throw invalidCredentialError();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let token;
|
|
50
|
+
/** @type {'base64' | 'plain'} */
|
|
51
|
+
let tokenEncoding;
|
|
52
|
+
if (encodingMarker === 'plain') {
|
|
53
|
+
token = encodedToken;
|
|
54
|
+
tokenEncoding = 'plain';
|
|
55
|
+
} else {
|
|
56
|
+
if (!base64Regex.test(encodedToken)) {
|
|
57
|
+
throw invalidCredentialError();
|
|
58
|
+
}
|
|
59
|
+
const tokenBuffer = Buffer.from(encodedToken, 'base64');
|
|
60
|
+
if (tokenBuffer.toString('base64') !== encodedToken) {
|
|
61
|
+
throw invalidCredentialError();
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
token = new TextDecoder('utf-8', { fatal: true }).decode(tokenBuffer);
|
|
65
|
+
} catch {
|
|
66
|
+
throw invalidCredentialError();
|
|
67
|
+
}
|
|
68
|
+
if (Buffer.from(token, 'utf8').toString('base64') !== encodedToken) {
|
|
69
|
+
throw invalidCredentialError();
|
|
70
|
+
}
|
|
71
|
+
tokenEncoding = 'base64';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
!token ||
|
|
76
|
+
token.length > 65536 ||
|
|
77
|
+
unsafeTokenRegex.test(token) ||
|
|
78
|
+
token.includes('${')
|
|
79
|
+
) {
|
|
80
|
+
throw invalidCredentialError();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
registryUrl,
|
|
85
|
+
token,
|
|
86
|
+
tokenEncoding,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {Record<string, string | undefined>} environmentArg
|
|
92
|
+
* @returns {Array<{
|
|
93
|
+
* registryUrl: string;
|
|
94
|
+
* token: string;
|
|
95
|
+
* tokenEncoding: 'base64' | 'plain';
|
|
96
|
+
* }>}
|
|
97
|
+
*/
|
|
98
|
+
export const parseNpmCredentialEnvironment = (environmentArg) => {
|
|
99
|
+
const credentialValues = Object.entries(environmentArg)
|
|
100
|
+
.filter(
|
|
101
|
+
([environmentVariableNameArg, environmentVariableValueArg]) =>
|
|
102
|
+
environmentVariableNameArg.startsWith('SZCI_TOKEN_NPM') &&
|
|
103
|
+
environmentVariableValueArg !== undefined,
|
|
104
|
+
)
|
|
105
|
+
.sort(([environmentVariableNameA], [environmentVariableNameB]) =>
|
|
106
|
+
environmentVariableNameA.localeCompare(environmentVariableNameB)
|
|
107
|
+
)
|
|
108
|
+
.map(([, environmentVariableValueArg]) => environmentVariableValueArg);
|
|
109
|
+
|
|
110
|
+
return credentialValues.map((credentialValueArg) => parseNpmCredential(credentialValueArg));
|
|
111
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface INpmCredential {
|
|
2
|
+
registryUrl: string;
|
|
3
|
+
token: string;
|
|
4
|
+
tokenEncoding: 'base64' | 'plain';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
parseNpmCredential as parseNpmCredentialShared,
|
|
9
|
+
parseNpmCredentialEnvironment as parseNpmCredentialEnvironmentShared,
|
|
10
|
+
validateNpmRegistryUrl as validateNpmRegistryUrlShared,
|
|
11
|
+
} from './npmcredential.shared.js';
|
|
12
|
+
|
|
13
|
+
export const validateNpmRegistryUrl = (
|
|
14
|
+
registryUrlArg: string,
|
|
15
|
+
): string => validateNpmRegistryUrlShared(registryUrlArg);
|
|
16
|
+
|
|
17
|
+
export const parseNpmCredential = (
|
|
18
|
+
npmEnvironmentValueArg: string,
|
|
19
|
+
): INpmCredential => parseNpmCredentialShared(npmEnvironmentValueArg);
|
|
20
|
+
|
|
21
|
+
export const parseNpmCredentialEnvironment = (
|
|
22
|
+
environmentArg: Record<string, string>,
|
|
23
|
+
): INpmCredential[] => parseNpmCredentialEnvironmentShared(environmentArg);
|