Skip to content

Commit fb98197

Browse files
committed
feat(scanner): add npm token based on registry for sdk calls
1 parent 49c5bbb commit fb98197

File tree

10 files changed

+265
-40
lines changed

10 files changed

+265
-40
lines changed

.changeset/forty-waves-jog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@nodesecure/scanner": minor
3+
---
4+
5+
feat(scanner): add npm token based on registry for sdk calls

package-lock.json

Lines changed: 57 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

workspaces/scanner/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@
7070
"type-fest": "^5.0.1"
7171
},
7272
"devDependencies": {
73+
"@npmcli/config": "^10.4.2",
7374
"@types/node": "^24.0.2",
75+
"@types/npmcli__config": "^6.0.3",
7476
"c8": "^10.1.3",
7577
"tsx": "^4.19.4",
7678
"typescript": "^5.8.3"

workspaces/scanner/src/depWalker.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ import { parseAuthor } from "@nodesecure/utils";
1414
import { ManifestManager, parseNpmSpec } from "@nodesecure/mama";
1515
import type { ManifestVersion, PackageJSON, WorkspacesPackageJSON } from "@nodesecure/npm-types";
1616
import { getNpmRegistryURL } from "@nodesecure/npm-registry-sdk";
17+
import type Config from "@npmcli/config";
1718

1819
// Import Internal Dependencies
1920
import {
2021
getDependenciesWarnings,
2122
addMissingVersionFlags,
2223
getUsedDeps,
23-
getManifestLinks
24+
getManifestLinks,
25+
NPM_TOKEN
2426
} from "./utils/index.js";
2527
import { NpmRegistryProvider } from "./registry/NpmRegistryProvider.js";
28+
import { RegistryTokenStore } from "./registry/RegistryTokenStore.js";
2629
import { TempDirectory } from "./class/TempDirectory.class.js";
2730
import { Logger, ScannerLoggerEvents } from "./class/logger.class.js";
2831
import type {
@@ -79,6 +82,7 @@ const { version: packageVersion } = JSON.parse(
7982
type WalkerOptions = Omit<Options, "registry"> & {
8083
registry: string;
8184
location?: string;
85+
npmRcConfig?: Config;
8286
};
8387

8488
export async function depWalker(
@@ -93,9 +97,12 @@ export async function depWalker(
9397
maxDepth,
9498
location,
9599
vulnerabilityStrategy = Vulnera.strategies.NONE,
96-
registry
100+
registry,
101+
npmRcConfig
97102
} = options;
98103

104+
const tokenStore = new RegistryTokenStore(npmRcConfig, NPM_TOKEN.token);
105+
99106
await using tempDir = await TempDirectory.create();
100107

101108
const dependencyConfusionWarnings: DependencyConfusionWarning[] = [];
@@ -150,7 +157,8 @@ export async function depWalker(
150157
const dep = dependencies.get(name)!;
151158
operationsQueue.push(
152159
new NpmRegistryProvider(name, version, {
153-
registry
160+
registry,
161+
tokenStore
154162
}).enrichDependencyVersion(dep, dependencyConfusionWarnings, org)
155163
);
156164

@@ -180,13 +188,17 @@ export async function depWalker(
180188
}
181189
else {
182190
fetchedMetadataPackages.add(name);
183-
const provider = new NpmRegistryProvider(name, version);
191+
const provider = new NpmRegistryProvider(name, version, {
192+
registry,
193+
tokenStore
194+
});
184195

185196
operationsQueue.push(provider.enrichDependency(logger, dependency));
186197
if (registry !== getNpmRegistryURL() && org) {
187198
operationsQueue.push(
188199
new NpmRegistryProvider(name, version, {
189-
registry
200+
registry,
201+
tokenStore
190202
}).enrichScopedDependencyConfusionWarnings(dependencyConfusionWarnings, org)
191203
);
192204
}

workspaces/scanner/src/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import pacote from "pacote";
88
import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk";
99
import * as tarball from "@nodesecure/tarball";
1010
import type { PackageJSON } from "@nodesecure/npm-types";
11+
import type Config from "@npmcli/config";
1112

1213
// Import Internal Dependencies
1314
import { depWalker } from "./depWalker.js";
@@ -27,9 +28,13 @@ const kDefaultCwdOptions = {
2728
export * from "./types.js";
2829
export * from "./extractors/index.js";
2930

31+
export type CwdOptions = Options & {
32+
npmRcConfig?: Config;
33+
};
34+
3035
export async function cwd(
3136
location = process.cwd(),
32-
options: Options = {},
37+
options: CwdOptions = {},
3338
logger = new Logger()
3439
) {
3540
const registry = options.registry ?

workspaces/scanner/src/registry/NpmRegistryProvider.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import { PackumentExtractor, type DateProvider } from "./PackumentExtractor.js";
1515
import { fetchNpmAvatars } from "./fetchNpmAvatars.js";
1616
import type {
1717
Dependency,
18-
DependencyConfusionWarning
18+
DependencyConfusionWarning,
19+
TokenStore
1920
} from "../types.js";
2021
import { Logger } from "../class/logger.class.js";
2122
import { getLinks } from "../utils/getLinks.js";
@@ -30,6 +31,7 @@ await i18n.extendFromSystemPath(
3031

3132
type PackumentNpmApiOptions = {
3233
registry: string;
34+
token?: string;
3335
};
3436

3537
export interface NpmApiClient {
@@ -42,12 +44,14 @@ export interface NpmRegistryProviderOptions {
4244
dateProvider?: DateProvider;
4345
npmApiClient?: NpmApiClient;
4446
registry?: string;
47+
tokenStore?: TokenStore;
4548
}
4649

4750
export class NpmRegistryProvider {
4851
#date: DateProvider | undefined;
4952
#npmApiClient: NpmApiClient;
5053
#registry: string;
54+
#tokenStore: TokenStore | undefined;
5155

5256
name: string;
5357
version: string;
@@ -60,7 +64,8 @@ export class NpmRegistryProvider {
6064
const {
6165
dateProvider = undefined,
6266
npmApiClient = npmRegistrySDK,
63-
registry = npmRegistrySDK.getLocalRegistryURL()
67+
registry = npmRegistrySDK.getLocalRegistryURL(),
68+
tokenStore = undefined
6469
} = options;
6570

6671
this.name = name;
@@ -69,14 +74,16 @@ export class NpmRegistryProvider {
6974
this.#date = dateProvider;
7075
this.#npmApiClient = npmApiClient;
7176
this.#registry = registry;
77+
this.#tokenStore = tokenStore;
7278
}
7379

7480
async collectPackageVersionData() {
7581
const packumentVersion = await this.#npmApiClient.packumentVersion(
7682
this.name,
7783
this.version,
7884
{
79-
registry: this.#registry
85+
registry: this.#registry,
86+
token: this.#tokenStore?.get(this.#registry)
8087
}
8188
);
8289

@@ -95,7 +102,8 @@ export class NpmRegistryProvider {
95102

96103
async collectPackageData() {
97104
const packument = await this.#npmApiClient.packument(this.name, {
98-
registry: this.#registry
105+
registry: this.#registry,
106+
token: this.#tokenStore?.get(this.#registry)
99107
});
100108
const packumentVersion = packument.versions[this.version];
101109

@@ -167,7 +175,8 @@ export class NpmRegistryProvider {
167175
}
168176
try {
169177
const packumentVersionFromPublicRegistry = await this.#npmApiClient.packumentVersion(this.name, this.version, {
170-
registry: getNpmRegistryURL()
178+
registry: getNpmRegistryURL(),
179+
token: this.#tokenStore?.get(getNpmRegistryURL())
171180
});
172181
if (!this.#hasSameSignatures(signatures, packumentVersionFromPublicRegistry.dist.signatures)) {
173182
this.#addDependencyConfusionWarning(warnings, await i18n.getToken("scanner.dependency_confusion"));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Import Third-party Dependencies
2+
import type Config from "@npmcli/config";
3+
4+
// Import Internal Dependencies
5+
import { type TokenStore } from "../types.js";
6+
7+
export class RegistryTokenStore implements TokenStore {
8+
#memo: Map<string, string | undefined> = new Map();
9+
#config: Config | undefined;
10+
#tokenFromEnv: string | undefined;
11+
constructor(config: Config | undefined, tokenFromEnv: string | undefined) {
12+
this.#config = config;
13+
this.#tokenFromEnv = tokenFromEnv;
14+
}
15+
16+
get(registry: string): string | undefined {
17+
if (!this.#config) {
18+
return this.#tokenFromEnv;
19+
}
20+
if (this.#memo.has(registry)) {
21+
return this.#memo.get(registry);
22+
}
23+
const token = this.#config.get(this.getTokenKey(registry), "project") as string | undefined ?? this.#tokenFromEnv;
24+
this.#memo.set(registry, token);
25+
26+
return token;
27+
}
28+
29+
private getTokenKey(registry: string) {
30+
return `${registry.replace(/https:|http:/, "")}:_authToken`;
31+
}
32+
}

workspaces/scanner/src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,10 @@ export interface Options {
262262
*/
263263
readonly scanRootNode?: boolean;
264264
}
265+
266+
export interface TokenStore {
267+
/**
268+
* Get the token for the given registry
269+
*/
270+
get(registry: string): string | undefined;
271+
}

0 commit comments

Comments
 (0)