@aws-sdk/find-v2 0.5.0 → 0.6.1

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 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 │ Runtime │ ContainsAwsSdkJsV2 │
38
- ├─────────────────────────────────────────┼───────────┼────────────┼────────────────────────────────────────────────┤
39
- │ fn-without-aws-sdk-in-bundle │ us-east-2 │ nodejs24.x │ No. │
40
- ├─────────────────────────────────────────┼───────────┼────────────┼────────────────────────────────────────────────┤
41
- │ fn-with-aws-sdk-in-bundle │ us-east-2 │ nodejs24.x │ Yes. Bundled in 'index.js' │
42
- ├─────────────────────────────────────────┼───────────┼────────────┼────────────────────────────────────────────────┤
43
- │ fn-with-aws-sdk-in-package-json-deps │ us-east-2 │ nodejs24.x │ Yes. Defined in dependencies of 'package.json' │
44
- ├─────────────────────────────────────────┼───────────┼────────────┼────────────────────────────────────────────────┤
45
- │ fn-without-aws-sdk-in-package-json-deps │ us-east-2 │ nodejs24.x │ 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
@@ -29,6 +29,15 @@ export const createProgram = () => {
29
29
  }
30
30
  return value;
31
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")
32
41
  .option("--region <region>", "AWS region to scan")
33
42
  .option("--profile <profile>", "AWS profile to use")
34
43
  .addOption(new Option("-o, --output <output>", "Output format")
@@ -6,7 +6,7 @@ import { getLambdaFunctionScanOutput } from "./utils/getLambdaFunctionScanOutput
6
6
  import { getLambdaNodeJsMatchingVersions } from "./utils/getLambdaNodeJsMatchingVersions.js";
7
7
  import { LambdaCommandOutputType, printLambdaCommandOutput, } from "./utils/printLambdaCommandOutput.js";
8
8
  export const scanLambdaFunctions = async (options) => {
9
- const { yes, node, region, profile, output, jobs } = options;
9
+ const { yes, node, sdk, region, profile, output, jobs } = options;
10
10
  const client = new Lambda({
11
11
  ...(region && { region }),
12
12
  ...(profile && { profile }),
@@ -43,6 +43,7 @@ export const scanLambdaFunctions = async (options) => {
43
43
  functionName: fn.FunctionName,
44
44
  region: clientRegion,
45
45
  runtime: fn.Runtime,
46
+ sdkVersionRange: sdk,
46
47
  }))));
47
48
  printLambdaCommandOutput(scanOutput, output);
48
49
  };
@@ -0,0 +1,3 @@
1
+ export const PACKAGE_JSON = "package.json";
2
+ export const NODE_MODULES = "node_modules";
3
+ export const AWS_SDK = "aws-sdk";
@@ -1,5 +1,6 @@
1
1
  import StreamZip from "node-stream-zip";
2
- const PACKAGE_JSON_FILENAME = "package.json";
2
+ import { AWS_SDK, NODE_MODULES, PACKAGE_JSON } from "./constants.js";
3
+ import { join } from "node:path";
3
4
  /**
4
5
  * Extracts the contents of a Lambda Function zip file.
5
6
  * Returns string contents of package.json files, if available.
@@ -11,6 +12,7 @@ const PACKAGE_JSON_FILENAME = "package.json";
11
12
  export const getLambdaFunctionContents = async (zipPath) => {
12
13
  const zip = new StreamZip.async({ file: zipPath });
13
14
  const packageJsonFiles = [];
15
+ const awsSdkPackageJsonMap = {};
14
16
  let zipEntries = {};
15
17
  try {
16
18
  zipEntries = await zip.entries();
@@ -20,11 +22,16 @@ export const getLambdaFunctionContents = async (zipPath) => {
20
22
  // ToDo: add warning when logging is supported in future.
21
23
  }
22
24
  for (const zipEntry of Object.values(zipEntries)) {
23
- // Skip 'node_modules' directory, as it's not the customer source code.
24
- if (zipEntry.name.includes("node_modules/"))
25
+ // Skip 'node_modules' directory, except for aws-sdk package.json file.
26
+ if (zipEntry.name.includes(`${NODE_MODULES}/`)) {
27
+ if (zipEntry.name.endsWith(join(NODE_MODULES, AWS_SDK, PACKAGE_JSON)) && zipEntry.isFile) {
28
+ const packageJsonContent = await zip.entryData(zipEntry.name);
29
+ awsSdkPackageJsonMap[zipEntry.name] = packageJsonContent.toString();
30
+ }
25
31
  continue;
32
+ }
26
33
  // Skip anything which is not 'package.json'
27
- if (!zipEntry.name.endsWith(PACKAGE_JSON_FILENAME))
34
+ if (!zipEntry.name.endsWith(PACKAGE_JSON))
28
35
  continue;
29
36
  // Skip if 'package.json' is not a file
30
37
  if (!zipEntry.isFile)
@@ -43,7 +50,10 @@ export const getLambdaFunctionContents = async (zipPath) => {
43
50
  }
44
51
  if (packageJsonFiles.length !== 0) {
45
52
  await zip.close();
46
- return { packageJsonFiles };
53
+ return {
54
+ packageJsonFiles,
55
+ ...(Object.keys(awsSdkPackageJsonMap).length > 0 && { awsSdkPackageJsonMap }),
56
+ };
47
57
  }
48
58
  for (const path of ["index.js", "index.mjs", "index.cjs"]) {
49
59
  if (!zipEntries[path])
@@ -1,14 +1,28 @@
1
+ import { satisfies, validate } 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
- import { join } from "node:path";
7
- export const getLambdaFunctionScanOutput = async (client, { functionName, region, runtime }) => {
7
+ import { dirname, join } from "node:path";
8
+ import { AWS_SDK, NODE_MODULES, PACKAGE_JSON } from "./constants.js";
9
+ /**
10
+ * Scans a Lambda function to detect AWS SDK for JavaScript v2 usage.
11
+ *
12
+ * Downloads the function code, extracts it, and checks for v2 SDK in:
13
+ * 1. package.json dependencies
14
+ * 2. Bundled index file
15
+ *
16
+ * @param client - AWS Lambda client instance
17
+ * @param options - Scan configuration options
18
+ * @returns Scan results including SDK v2 detection status and location
19
+ */
20
+ export const getLambdaFunctionScanOutput = async (client, { functionName, region, runtime, sdkVersionRange }) => {
8
21
  const output = {
9
22
  FunctionName: functionName,
10
23
  Region: region,
11
24
  Runtime: runtime,
25
+ SdkVersion: sdkVersionRange,
12
26
  ContainsAwsSdkJsV2: null,
13
27
  };
14
28
  const response = await client.getFunction({ FunctionName: functionName });
@@ -23,23 +37,54 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
23
37
  lambdaFunctionContents = await getLambdaFunctionContents(zipPath);
24
38
  }
25
39
  catch (error) {
40
+ const errorPrefix = "Error downloading or reading Lambda function code";
26
41
  output.AwsSdkJsV2Error =
27
- error instanceof Error
28
- ? `Error downloading or reading Lambda function code: ${error.message}`
29
- : "Error downloading or reading Lambda function code.";
42
+ error instanceof Error ? `${errorPrefix}: ${error.message}` : errorPrefix;
30
43
  return output;
31
44
  }
32
45
  finally {
33
46
  await rm(zipPath, { force: true });
34
47
  }
35
- const { packageJsonFiles, bundleFile } = lambdaFunctionContents;
36
- // Search for "aws-sdk" in package.json dependencies if present.
48
+ const { packageJsonFiles, awsSdkPackageJsonMap, bundleFile } = lambdaFunctionContents;
49
+ // Search for JS SDK v2 in package.json dependencies if present.
37
50
  if (packageJsonFiles && packageJsonFiles.length > 0) {
38
51
  for (const { path: packageJsonPath, content: packageJsonContent } of packageJsonFiles) {
39
52
  try {
40
53
  const packageJson = JSON.parse(packageJsonContent);
41
54
  const dependencies = packageJson.dependencies || {};
42
- if ("aws-sdk" in dependencies) {
55
+ if (AWS_SDK in dependencies) {
56
+ const awsSdkVersionInPackageJson = dependencies[AWS_SDK];
57
+ const awsSdkPackageJsonPathInNodeModules = join(NODE_MODULES, AWS_SDK, PACKAGE_JSON);
58
+ // Get aws-sdk package.json from nested node_modules or root node_modules.
59
+ const awsSdkPackageJson = awsSdkPackageJsonMap
60
+ ? (awsSdkPackageJsonMap[join(dirname(packageJsonPath), awsSdkPackageJsonPathInNodeModules)] ?? awsSdkPackageJsonMap[awsSdkPackageJsonPathInNodeModules])
61
+ : undefined;
62
+ let awsSdkVersionInNodeModules;
63
+ try {
64
+ if (awsSdkPackageJson) {
65
+ awsSdkVersionInNodeModules = JSON.parse(awsSdkPackageJson).version;
66
+ }
67
+ }
68
+ catch {
69
+ // Skip if JSON can't be parsed.
70
+ // ToDo: add warning when logging is supported in future.
71
+ }
72
+ const sdkVersionToCheck = validate(awsSdkVersionInPackageJson) || awsSdkPackageJson === undefined
73
+ ? // Use version in package.json dependencies, if fixed version is defined or aws-sdk package.json is not available.
74
+ awsSdkVersionInPackageJson
75
+ : // Use version from aws-sdk package.json, if defined
76
+ (awsSdkVersionInNodeModules ?? awsSdkVersionInPackageJson);
77
+ try {
78
+ if (!satisfies(sdkVersionToCheck, sdkVersionRange)) {
79
+ continue;
80
+ }
81
+ }
82
+ catch (error) {
83
+ const errorPrefix = `Error checking version range '${sdkVersionRange}' for aws-sdk@${dependencies["aws-sdk"]} in '${packageJsonPath}'`;
84
+ output.AwsSdkJsV2Error =
85
+ error instanceof Error ? `${errorPrefix}: ${error.message}` : errorPrefix;
86
+ return output;
87
+ }
43
88
  output.ContainsAwsSdkJsV2 = true;
44
89
  output.AwsSdkJsV2Location = `Defined in dependencies of '${packageJsonPath}'`;
45
90
  return output;
@@ -53,13 +98,23 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
53
98
  }
54
99
  }
55
100
  }
56
- // Check for code of "aws-sdk" in bundle, if not found in package.json dependencies.
57
- if (bundleFile && hasSdkV2InBundle(bundleFile.content)) {
58
- output.ContainsAwsSdkJsV2 = true;
59
- output.AwsSdkJsV2Location = `Bundled in '${bundleFile.path}'`;
60
- return output;
101
+ // Check for signature of JS SDK v2 in bundle, if not found in package.json dependencies.
102
+ if (bundleFile) {
103
+ try {
104
+ if (hasSdkV2InBundle(bundleFile.content, sdkVersionRange)) {
105
+ output.ContainsAwsSdkJsV2 = true;
106
+ output.AwsSdkJsV2Location = `Bundled in '${bundleFile.path}'`;
107
+ return output;
108
+ }
109
+ }
110
+ catch (error) {
111
+ const errorPrefix = `Error reading bundle '${bundleFile.path}' for aws-sdk@${sdkVersionRange}`;
112
+ output.AwsSdkJsV2Error =
113
+ error instanceof Error ? `${errorPrefix}: ${error.message}` : errorPrefix;
114
+ return output;
115
+ }
61
116
  }
62
- // "aws-sdk" dependency/code not found.
117
+ // JS SDK v2 dependency/code not found.
63
118
  output.ContainsAwsSdkJsV2 = false;
64
119
  return output;
65
120
  };
@@ -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
- return true;
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", "Runtime", "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([scanOutput.FunctionName, scanOutput.Region, scanOutput.Runtime, notes]);
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.5.0",
3
+ "version": "0.6.1",
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",
@@ -25,12 +25,12 @@
25
25
  "@tsconfig/node-ts": "^23.6.2",
26
26
  "@tsconfig/node20": "^20.1.8",
27
27
  "@types/node": "^20.14.8",
28
- "aws-sdk": "^2.1692.0",
28
+ "aws-sdk": "2.1693.0",
29
29
  "esbuild": "~0.27.1",
30
- "oxfmt": "^0.18.0",
30
+ "oxfmt": "^0.21.0",
31
31
  "oxlint": "^1.33.0",
32
32
  "parcel": "^2.16.3",
33
- "rolldown": "1.0.0-beta.54",
33
+ "rolldown": "1.0.0-beta.58",
34
34
  "rollup": "^4.53.3",
35
35
  "typescript": "^5.9.3",
36
36
  "vitest": "^4.0.15",