@aws-sdk/find-v2 0.4.3 → 0.6.0
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/README.md +11 -11
- package/dist/cli.js +21 -2
- package/dist/scanLambdaFunctions.js +13 -5
- package/dist/utils/getLambdaFunctionScanOutput.js +42 -8
- package/dist/utils/getLambdaFunctions.js +5 -3
- package/dist/utils/getLambdaNodeJsMatchingVersions.js +17 -0
- package/dist/utils/hasSdkV2InBundle.js +13 -2
- package/dist/utils/printLambdaCommandOutput.js +8 -2
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -33,17 +33,17 @@ Run `lambda` command to scan Lambda Node.js Functions for JavaScript SDK v2.
|
|
|
33
33
|
|
|
34
34
|
```console
|
|
35
35
|
$ npx @aws-sdk/find-v2 lambda --yes --output table
|
|
36
|
-
|
|
37
|
-
│ FunctionName │ Region │ ContainsAwsSdkJsV2 │
|
|
38
|
-
|
|
39
|
-
│ fn-without-aws-sdk-in-bundle │ us-east-2 │ No. │
|
|
40
|
-
|
|
41
|
-
│ fn-with-aws-sdk-in-bundle │ us-east-2 │ Yes. Bundled in 'index.js' │
|
|
42
|
-
|
|
43
|
-
│ fn-with-aws-sdk-in-package-json-deps │ us-east-2 │ Yes. Defined in dependencies of 'package.json' │
|
|
44
|
-
|
|
45
|
-
│ fn-without-aws-sdk-in-package-json-deps │ us-east-2 │ No. │
|
|
46
|
-
|
|
36
|
+
┌─────────────────────────────────────────┬───────────┬────────────┬────────────┬────────────────────────────────────────────────┐
|
|
37
|
+
│ FunctionName │ Region │ Runtime │ SdkVersion │ ContainsAwsSdkJsV2 │
|
|
38
|
+
├─────────────────────────────────────────┼───────────┼────────────┼────────────┼────────────────────────────────────────────────┤
|
|
39
|
+
│ fn-without-aws-sdk-in-bundle │ us-east-2 │ nodejs24.x │ >=2.0.0 │ No. │
|
|
40
|
+
├─────────────────────────────────────────┼───────────┼────────────┼────────────┼────────────────────────────────────────────────┤
|
|
41
|
+
│ fn-with-aws-sdk-in-bundle │ us-east-2 │ nodejs24.x │ >=2.0.0 │ Yes. Bundled in 'index.js' │
|
|
42
|
+
├─────────────────────────────────────────┼───────────┼────────────┼────────────┼────────────────────────────────────────────────┤
|
|
43
|
+
│ fn-with-aws-sdk-in-package-json-deps │ us-east-2 │ nodejs24.x │ >=2.0.0 │ Yes. Defined in dependencies of 'package.json' │
|
|
44
|
+
├─────────────────────────────────────────┼───────────┼────────────┼────────────┼────────────────────────────────────────────────┤
|
|
45
|
+
│ fn-without-aws-sdk-in-package-json-deps │ us-east-2 │ nodejs24.x │ >=2.0.0 │ No. │
|
|
46
|
+
└─────────────────────────────────────────┴───────────┴────────────┴────────────┴────────────────────────────────────────────────┘
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
This script requires AWS Managed Policy [AWSLambda_ReadOnlyAccess][].
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
|
+
import { satisfies } from "compare-versions";
|
|
2
3
|
import { cpus } from "node:os";
|
|
3
4
|
import packageJson from "../package.json" with { type: "json" };
|
|
4
5
|
import { scanLambdaFunctions } from "./scanLambdaFunctions.js";
|
|
@@ -19,8 +20,26 @@ export const createProgram = () => {
|
|
|
19
20
|
.command("lambda")
|
|
20
21
|
.description("Scans Lambda Node.js Functions for JavaScript SDK v2")
|
|
21
22
|
.option("-y, --yes", "answer yes for all prompts", false)
|
|
22
|
-
.option("
|
|
23
|
-
|
|
23
|
+
.option("--node <semver>", "Semver range string to select Lambda Node.js major versions", (value) => {
|
|
24
|
+
try {
|
|
25
|
+
satisfies("0", value);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new Error(`Invalid semver range: ${value}`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}, ">=20")
|
|
32
|
+
.option("--sdk <semver>", "Semver range string to check for AWS SDK for JavaScript v2", (value) => {
|
|
33
|
+
try {
|
|
34
|
+
satisfies("0", value);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error(`Invalid semver range: ${value}`);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}, ">=2.0.0")
|
|
41
|
+
.option("--region <region>", "AWS region to scan")
|
|
42
|
+
.option("--profile <profile>", "AWS profile to use")
|
|
24
43
|
.addOption(new Option("-o, --output <output>", "Output format")
|
|
25
44
|
.choices(Object.values(LambdaCommandOutputType))
|
|
26
45
|
.default(LambdaCommandOutputType.json))
|
|
@@ -3,14 +3,20 @@ import pLimit from "p-limit";
|
|
|
3
3
|
import { getDownloadConfirmation } from "./utils/getDownloadConfirmation.js";
|
|
4
4
|
import { getLambdaFunctions } from "./utils/getLambdaFunctions.js";
|
|
5
5
|
import { getLambdaFunctionScanOutput } from "./utils/getLambdaFunctionScanOutput.js";
|
|
6
|
+
import { getLambdaNodeJsMatchingVersions } from "./utils/getLambdaNodeJsMatchingVersions.js";
|
|
6
7
|
import { LambdaCommandOutputType, printLambdaCommandOutput, } from "./utils/printLambdaCommandOutput.js";
|
|
7
8
|
export const scanLambdaFunctions = async (options) => {
|
|
8
|
-
const { yes, region, profile, output, jobs } = options;
|
|
9
|
+
const { yes, node, sdk, region, profile, output, jobs } = options;
|
|
9
10
|
const client = new Lambda({
|
|
10
11
|
...(region && { region }),
|
|
11
12
|
...(profile && { profile }),
|
|
12
13
|
});
|
|
13
|
-
const
|
|
14
|
+
const lambdaNodeJsMajorVersions = getLambdaNodeJsMatchingVersions(node);
|
|
15
|
+
if (lambdaNodeJsMajorVersions.length === 0) {
|
|
16
|
+
printLambdaCommandOutput([], output);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const functions = await getLambdaFunctions(client, lambdaNodeJsMajorVersions);
|
|
14
20
|
const functionCount = functions.length;
|
|
15
21
|
const concurrency = Math.min(functionCount, jobs || 1);
|
|
16
22
|
const codeSizeToDownload = functions.reduce((acc, fn) => acc + (fn.CodeSize || 0), 0);
|
|
@@ -20,15 +26,15 @@ export const scanLambdaFunctions = async (options) => {
|
|
|
20
26
|
.slice(0, concurrency)
|
|
21
27
|
.reduce((acc, size) => acc + size, 0);
|
|
22
28
|
if (functionCount === 0) {
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
printLambdaCommandOutput([], output);
|
|
30
|
+
return;
|
|
25
31
|
}
|
|
26
32
|
if (!yes) {
|
|
27
33
|
const confirmation = await getDownloadConfirmation(functionCount, codeSizeToDownload, codeSizeToSaveOnDisk);
|
|
28
34
|
console.log();
|
|
29
35
|
if (!confirmation) {
|
|
30
36
|
console.log("Exiting.");
|
|
31
|
-
|
|
37
|
+
return;
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
40
|
const clientRegion = await client.config.region();
|
|
@@ -36,6 +42,8 @@ export const scanLambdaFunctions = async (options) => {
|
|
|
36
42
|
const scanOutput = await Promise.all(functions.map((fn) => limit(() => getLambdaFunctionScanOutput(client, {
|
|
37
43
|
functionName: fn.FunctionName,
|
|
38
44
|
region: clientRegion,
|
|
45
|
+
runtime: fn.Runtime,
|
|
46
|
+
sdkVersionRange: sdk,
|
|
39
47
|
}))));
|
|
40
48
|
printLambdaCommandOutput(scanOutput, output);
|
|
41
49
|
};
|
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
import { satisfies } from "compare-versions";
|
|
1
2
|
import { downloadFile } from "./downloadFile.js";
|
|
2
3
|
import { getLambdaFunctionContents, } from "./getLambdaFunctionContents.js";
|
|
3
4
|
import { hasSdkV2InBundle } from "./hasSdkV2InBundle.js";
|
|
4
5
|
import { rm } from "node:fs/promises";
|
|
5
6
|
import { tmpdir } from "node:os";
|
|
6
7
|
import { join } from "node:path";
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Scans a Lambda function to detect AWS SDK for JavaScript v2 usage.
|
|
10
|
+
*
|
|
11
|
+
* Downloads the function code, extracts it, and checks for v2 SDK in:
|
|
12
|
+
* 1. package.json dependencies
|
|
13
|
+
* 2. Bundled index file
|
|
14
|
+
*
|
|
15
|
+
* @param client - AWS Lambda client instance
|
|
16
|
+
* @param options - Scan configuration options
|
|
17
|
+
* @returns Scan results including SDK v2 detection status and location
|
|
18
|
+
*/
|
|
19
|
+
export const getLambdaFunctionScanOutput = async (client, { functionName, region, runtime, sdkVersionRange }) => {
|
|
8
20
|
const output = {
|
|
9
21
|
FunctionName: functionName,
|
|
10
22
|
Region: region,
|
|
23
|
+
Runtime: runtime,
|
|
24
|
+
SdkVersion: sdkVersionRange,
|
|
11
25
|
ContainsAwsSdkJsV2: null,
|
|
12
26
|
};
|
|
13
27
|
const response = await client.getFunction({ FunctionName: functionName });
|
|
@@ -22,10 +36,9 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
|
|
|
22
36
|
lambdaFunctionContents = await getLambdaFunctionContents(zipPath);
|
|
23
37
|
}
|
|
24
38
|
catch (error) {
|
|
39
|
+
const errorPrefix = "Error downloading or reading Lambda function code";
|
|
25
40
|
output.AwsSdkJsV2Error =
|
|
26
|
-
error instanceof Error
|
|
27
|
-
? `Error downloading or reading Lambda function code: ${error.message}`
|
|
28
|
-
: "Error downloading or reading Lambda function code.";
|
|
41
|
+
error instanceof Error ? `${errorPrefix}: ${error.message}` : errorPrefix;
|
|
29
42
|
return output;
|
|
30
43
|
}
|
|
31
44
|
finally {
|
|
@@ -39,6 +52,17 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
|
|
|
39
52
|
const packageJson = JSON.parse(packageJsonContent);
|
|
40
53
|
const dependencies = packageJson.dependencies || {};
|
|
41
54
|
if ("aws-sdk" in dependencies) {
|
|
55
|
+
try {
|
|
56
|
+
if (!satisfies(dependencies["aws-sdk"], sdkVersionRange)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const errorPrefix = `Error checking version range '${sdkVersionRange}' for aws-sdk@${dependencies["aws-sdk"]} in '${packageJsonPath}'`;
|
|
62
|
+
output.AwsSdkJsV2Error =
|
|
63
|
+
error instanceof Error ? `${errorPrefix}: ${error.message}` : errorPrefix;
|
|
64
|
+
return output;
|
|
65
|
+
}
|
|
42
66
|
output.ContainsAwsSdkJsV2 = true;
|
|
43
67
|
output.AwsSdkJsV2Location = `Defined in dependencies of '${packageJsonPath}'`;
|
|
44
68
|
return output;
|
|
@@ -53,10 +77,20 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
|
|
|
53
77
|
}
|
|
54
78
|
}
|
|
55
79
|
// Check for code of "aws-sdk" in bundle, if not found in package.json dependencies.
|
|
56
|
-
if (bundleFile
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
if (bundleFile) {
|
|
81
|
+
try {
|
|
82
|
+
if (hasSdkV2InBundle(bundleFile.content, sdkVersionRange)) {
|
|
83
|
+
output.ContainsAwsSdkJsV2 = true;
|
|
84
|
+
output.AwsSdkJsV2Location = `Bundled in '${bundleFile.path}'`;
|
|
85
|
+
return output;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const errorPrefix = `Error reading bundle '${bundleFile.path}' for aws-sdk@${sdkVersionRange}`;
|
|
90
|
+
output.AwsSdkJsV2Error =
|
|
91
|
+
error instanceof Error ? `${errorPrefix}: ${error.message}` : errorPrefix;
|
|
92
|
+
return output;
|
|
93
|
+
}
|
|
60
94
|
}
|
|
61
95
|
// "aws-sdk" dependency/code not found.
|
|
62
96
|
output.ContainsAwsSdkJsV2 = false;
|
|
@@ -3,17 +3,19 @@ import { paginateListFunctions } from "@aws-sdk/client-lambda";
|
|
|
3
3
|
* Retrieves all Lambda functions with Node.js runtime from AWS
|
|
4
4
|
*
|
|
5
5
|
* @param client - AWS Lambda client instance
|
|
6
|
+
* @param lambdaNodeJsMajorVersions - Array of Node.js major versions to filter (e.g., ["18", "20"])
|
|
6
7
|
* @returns Promise that resolves to array of Lambda function configurations
|
|
7
8
|
* @description
|
|
8
9
|
* - Uses AWS SDK v3 pagination to handle large number of functions
|
|
9
|
-
* - Filters results to only include Node.js runtimes
|
|
10
|
+
* - Filters results to only include specified Node.js runtimes
|
|
10
11
|
* - Returns empty array if no functions found
|
|
11
12
|
*/
|
|
12
|
-
export const getLambdaFunctions = async (client) => {
|
|
13
|
+
export const getLambdaFunctions = async (client, lambdaNodeJsMajorVersions) => {
|
|
13
14
|
const functions = [];
|
|
15
|
+
const lambdaNodeJsIdentifiers = lambdaNodeJsMajorVersions.map((version) => `nodejs${version}.x`);
|
|
14
16
|
const paginator = paginateListFunctions({ client }, {});
|
|
15
17
|
for await (const page of paginator) {
|
|
16
|
-
functions.push(...(page.Functions ?? []).filter((fn) => fn.Runtime
|
|
18
|
+
functions.push(...(page.Functions ?? []).filter((fn) => lambdaNodeJsIdentifiers.includes(fn.Runtime ?? "")));
|
|
17
19
|
}
|
|
18
20
|
return functions;
|
|
19
21
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { satisfies } from "compare-versions";
|
|
2
|
+
// Refs: https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
|
|
3
|
+
export const LambdaNodeJsMajorVersions = ["10", "12", "14", "16", "18", "20", "22", "24"];
|
|
4
|
+
/**
|
|
5
|
+
* Returns Lambda Node.js major versions that satisfy the given semver range.
|
|
6
|
+
* @param semverRange - A valid semver range string (e.g., ">=18")
|
|
7
|
+
* @returns Array of matching Node.js major version strings supported by Lambda
|
|
8
|
+
*/
|
|
9
|
+
export const getLambdaNodeJsMatchingVersions = (semverRange) => {
|
|
10
|
+
const matchingVersions = [];
|
|
11
|
+
for (const nodejsVersion of LambdaNodeJsMajorVersions) {
|
|
12
|
+
if (satisfies(nodejsVersion, semverRange)) {
|
|
13
|
+
matchingVersions.push(nodejsVersion);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return matchingVersions;
|
|
17
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { satisfies } from "compare-versions";
|
|
1
2
|
const AWS_SDK_ENV_VARS = [
|
|
2
3
|
"AWS_CONFIG_FILE",
|
|
3
4
|
// "AWS_CONTAINER_AUTHORIZATION_TOKEN", // Tree shaken by esbuild
|
|
@@ -32,13 +33,23 @@ const AWS_SDK_ENV_VARS = [
|
|
|
32
33
|
* Checks if AWS SDK v2 is present in the provided bundle content by looking for specific environment variables.
|
|
33
34
|
*
|
|
34
35
|
* @param bundleContent - The string content of the bundle to check.
|
|
36
|
+
* @param sdkVersionRange - Semver range string to check for AWS SDK for JavaScript v2
|
|
35
37
|
* @returns boolean - Returns true if all AWS SDK v2 environment variables are found in the bundle content, false otherwise.
|
|
36
38
|
*/
|
|
37
|
-
export const hasSdkV2InBundle = (bundleContent) => {
|
|
39
|
+
export const hasSdkV2InBundle = (bundleContent, sdkVersionRange) => {
|
|
38
40
|
for (const envVar of AWS_SDK_ENV_VARS) {
|
|
39
41
|
if (!bundleContent.includes(envVar)) {
|
|
40
42
|
return false;
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
|
-
|
|
45
|
+
// Get version number from `VERSION:'2.X.Y'` or `VERSION: '2.X.Y'`, including double quotes and backticks.
|
|
46
|
+
const matches = bundleContent.match(/VERSION:\s*(['"`])(2\.\d+\.\d+)\1/);
|
|
47
|
+
if (matches && matches[2]) {
|
|
48
|
+
const version = matches[2];
|
|
49
|
+
// If version is in the specified range, return true
|
|
50
|
+
if (satisfies(version, sdkVersionRange)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
44
55
|
};
|
|
@@ -13,7 +13,7 @@ export const printLambdaCommandOutput = (output, outputType) => {
|
|
|
13
13
|
}
|
|
14
14
|
// Output as table
|
|
15
15
|
const table = new Table({
|
|
16
|
-
head: ["FunctionName", "Region", "ContainsAwsSdkJsV2"],
|
|
16
|
+
head: ["FunctionName", "Region", "Runtime", "SdkVersion", "ContainsAwsSdkJsV2"],
|
|
17
17
|
style: { head: ["bold"] },
|
|
18
18
|
});
|
|
19
19
|
for (const scanOutput of output) {
|
|
@@ -28,7 +28,13 @@ export const printLambdaCommandOutput = (output, outputType) => {
|
|
|
28
28
|
if (scanOutput.AwsSdkJsV2Location !== undefined) {
|
|
29
29
|
notes += ` ${scanOutput.AwsSdkJsV2Location}`;
|
|
30
30
|
}
|
|
31
|
-
table.push([
|
|
31
|
+
table.push([
|
|
32
|
+
scanOutput.FunctionName,
|
|
33
|
+
scanOutput.Region,
|
|
34
|
+
scanOutput.Runtime,
|
|
35
|
+
scanOutput.SdkVersion,
|
|
36
|
+
notes,
|
|
37
|
+
]);
|
|
32
38
|
}
|
|
33
39
|
console.log(table.toString());
|
|
34
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-sdk/find-v2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "CLI to find resources which call AWS using JavaScript SDK v2",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"types": "dist/cli.d.ts",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"@aws-sdk/client-lambda": "^3.942.0",
|
|
10
10
|
"cli-table3": "^0.6.5",
|
|
11
11
|
"commander": "^14.0.2",
|
|
12
|
+
"compare-versions": "^6.1.1",
|
|
12
13
|
"node-stream-zip": "^1.15.0",
|
|
13
14
|
"p-limit": "^7.2.0"
|
|
14
15
|
},
|
|
@@ -24,12 +25,12 @@
|
|
|
24
25
|
"@tsconfig/node-ts": "^23.6.2",
|
|
25
26
|
"@tsconfig/node20": "^20.1.8",
|
|
26
27
|
"@types/node": "^20.14.8",
|
|
27
|
-
"aws-sdk": "
|
|
28
|
+
"aws-sdk": "2.1693.0",
|
|
28
29
|
"esbuild": "~0.27.1",
|
|
29
|
-
"oxfmt": "^0.
|
|
30
|
+
"oxfmt": "^0.21.0",
|
|
30
31
|
"oxlint": "^1.33.0",
|
|
31
32
|
"parcel": "^2.16.3",
|
|
32
|
-
"rolldown": "1.0.0-beta.
|
|
33
|
+
"rolldown": "1.0.0-beta.58",
|
|
33
34
|
"rollup": "^4.53.3",
|
|
34
35
|
"typescript": "^5.9.3",
|
|
35
36
|
"vitest": "^4.0.15",
|