spectrum-cli 6.0.2 → 6.0.4
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/CHANGELOG.md +12 -0
- package/README.md +10 -5
- package/package.json +1 -1
- package/src/security/runtime.js +0 -39
- package/src/security/scan.js +21 -3
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,18 @@ This is the changelog of the `spectrum-cli` npm package. Its version comes from
|
|
|
9
9
|
`package.json`; the release commands use Git tags and `CHANGELOG.md` in the
|
|
10
10
|
**target project** and do not update that project's `package.json`.
|
|
11
11
|
|
|
12
|
+
## 🚀 [6.0.4] - 2026-10-29
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- В Skopeo передаётся ссылка repository@digest без tag: устранена ошибка Docker references with both a tag and digest. Исходный tag сохраняется в отчёте для поиска обновлений runtime; порт registry не изменяется.
|
|
17
|
+
|
|
18
|
+
## 🚀 [6.0.3] - 2026-10-29
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Начальный и повторный npm audit явно используют https://registry.npmjs.org/; удалены Nexus-авторизация аудита и глобальная подмена npm registry. Установка зависимостей сохраняет настройки проекта.
|
|
23
|
+
|
|
12
24
|
## 🚀 [6.0.2] - 2026-10-29
|
|
13
25
|
|
|
14
26
|
### Fixed
|
package/README.md
CHANGED
|
@@ -614,11 +614,16 @@ fixtures получены из прежнего Python-кода; исполня
|
|
|
614
614
|
|
|
615
615
|
### npm registry и проверка runtime
|
|
616
616
|
|
|
617
|
-
`
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
617
|
+
`security scan` и повторный audit после исправлений всегда вызывают:
|
|
618
|
+
|
|
619
|
+
```sh
|
|
620
|
+
npm audit --registry=https://registry.npmjs.org/ --omit=dev --audit-level=high --json
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Публичный registry указан прямо в argv команды, как в локальном алиасе `na`. Nexus-авторизация для аудита
|
|
624
|
+
не создаётся. Настройки registry в проектном `.npmrc` и user config не меняются: установка и обновление
|
|
625
|
+
зависимостей продолжают использовать конфигурацию проекта. Порог ночного процесса — HIGH/CRITICAL.
|
|
626
|
+
Ошибки audit сохраняют HTTP-код и причину с удалением credentials из диагностических сообщений.
|
|
622
627
|
|
|
623
628
|
Для версии Node из runtime нужны Skopeo, umoci и chroot. CLI копирует выбранную платформу immutable OCI-образа,
|
|
624
629
|
распаковывает rootfs в rootless-режиме и запускает только настроенный Node binary с `--version` от uid/gid 65534.
|
package/package.json
CHANGED
package/src/security/runtime.js
CHANGED
|
@@ -90,45 +90,6 @@ async function registryAuth(env, action) {
|
|
|
90
90
|
).toString("base64"),
|
|
91
91
|
};
|
|
92
92
|
const runtime = { ...env };
|
|
93
|
-
if (env.NIGHTLY_NPM_REGISTRY && env.NIGHTLY_NPM_ENABLED !== "false") {
|
|
94
|
-
let registry;
|
|
95
|
-
try {
|
|
96
|
-
registry = new URL(env.NIGHTLY_NPM_REGISTRY);
|
|
97
|
-
} catch {
|
|
98
|
-
throw new Error("NIGHTLY_NPM_REGISTRY must be an HTTPS registry URL");
|
|
99
|
-
}
|
|
100
|
-
ensure(
|
|
101
|
-
registry.protocol === "https:" &&
|
|
102
|
-
!registry.username &&
|
|
103
|
-
!registry.password &&
|
|
104
|
-
!registry.search &&
|
|
105
|
-
!registry.hash,
|
|
106
|
-
"NIGHTLY_NPM_REGISTRY must be an HTTPS registry URL",
|
|
107
|
-
);
|
|
108
|
-
runtime.NPM_CONFIG_REGISTRY = registry.href;
|
|
109
|
-
if (env.NEXUS_USER && env.NEXUS_PASSWORD) {
|
|
110
|
-
const userconfig =
|
|
111
|
-
env.NPM_CONFIG_USERCONFIG ||
|
|
112
|
-
env.npm_config_userconfig ||
|
|
113
|
-
path.join(require("node:os").homedir(), ".npmrc");
|
|
114
|
-
const existing = fs.existsSync(userconfig)
|
|
115
|
-
? fs.readFileSync(userconfig, "utf8")
|
|
116
|
-
: "";
|
|
117
|
-
const scope =
|
|
118
|
-
"//" + registry.host + registry.pathname.replace(/\/?$/, "/");
|
|
119
|
-
const auth = Buffer.from(
|
|
120
|
-
env.NEXUS_USER + ":" + env.NEXUS_PASSWORD,
|
|
121
|
-
).toString("base64");
|
|
122
|
-
const file = path.join(directory, "npmrc");
|
|
123
|
-
fs.writeFileSync(
|
|
124
|
-
file,
|
|
125
|
-
existing.trimEnd() + "\n" + scope + ":_auth=" + auth + "\n",
|
|
126
|
-
{ mode: 0o600 },
|
|
127
|
-
);
|
|
128
|
-
runtime.NPM_CONFIG_USERCONFIG = file;
|
|
129
|
-
delete runtime.npm_config_userconfig;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
93
|
if (Object.keys(auths).length) {
|
|
133
94
|
const file = path.join(directory, "config.json");
|
|
134
95
|
fs.writeFileSync(file, JSON.stringify({ auths }), { mode: 0o600 });
|
package/src/security/scan.js
CHANGED
|
@@ -59,6 +59,17 @@ function runtimeFromDockerfile(text) {
|
|
|
59
59
|
ensure(last, "No runtime FROM found");
|
|
60
60
|
return last;
|
|
61
61
|
}
|
|
62
|
+
function skopeoImageReference(image) {
|
|
63
|
+
const at = image.indexOf("@");
|
|
64
|
+
if (at === -1) return image;
|
|
65
|
+
const name = image.slice(0, at),
|
|
66
|
+
digest = image.slice(at + 1);
|
|
67
|
+
ensure(DIGEST.test(digest), "Invalid runtime image digest");
|
|
68
|
+
const colon = name.lastIndexOf(":"),
|
|
69
|
+
slash = name.lastIndexOf("/");
|
|
70
|
+
// Strip the tag, never the registry port. Keep the original tag in report metadata.
|
|
71
|
+
return (colon > slash ? name.slice(0, colon) : name) + "@" + digest;
|
|
72
|
+
}
|
|
62
73
|
class Scanner {
|
|
63
74
|
constructor(env = process.env, deps = {}) {
|
|
64
75
|
this.env = env;
|
|
@@ -69,7 +80,14 @@ class Scanner {
|
|
|
69
80
|
}
|
|
70
81
|
async auditNpm(root, evidence) {
|
|
71
82
|
const proc = this.run(
|
|
72
|
-
[
|
|
83
|
+
[
|
|
84
|
+
"npm",
|
|
85
|
+
"audit",
|
|
86
|
+
"--registry=https://registry.npmjs.org/",
|
|
87
|
+
"--omit=dev",
|
|
88
|
+
"--audit-level=high",
|
|
89
|
+
"--json",
|
|
90
|
+
],
|
|
73
91
|
root,
|
|
74
92
|
{ allowed: [0, 1] },
|
|
75
93
|
);
|
|
@@ -114,7 +132,7 @@ class Scanner {
|
|
|
114
132
|
"inspect",
|
|
115
133
|
"--format",
|
|
116
134
|
"{{.Digest}}",
|
|
117
|
-
"docker://" + image,
|
|
135
|
+
"docker://" + skopeoImageReference(image),
|
|
118
136
|
]).stdout.trim();
|
|
119
137
|
ensure(DIGEST.test(digest), "Registry did not return an image digest");
|
|
120
138
|
return image.split("@")[0] + "@" + digest;
|
|
@@ -136,7 +154,7 @@ class Scanner {
|
|
|
136
154
|
"--override-arch",
|
|
137
155
|
platform.split("/")[1],
|
|
138
156
|
"copy",
|
|
139
|
-
"docker://" + image,
|
|
157
|
+
"docker://" + skopeoImageReference(image),
|
|
140
158
|
"oci:" + imagePath + ":runtime",
|
|
141
159
|
]);
|
|
142
160
|
this.run([
|