@aws-sdk/find-v2 0.6.1 → 0.7.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 +13 -11
- package/dist/utils/getLambdaFunctionContents.js +29 -41
- package/dist/utils/getLambdaFunctionScanOutput.js +36 -22
- package/dist/utils/getPossibleHandlerFiles.js +9 -0
- package/dist/utils/hasSdkV2InFile.js +45 -0
- package/dist/utils/printLambdaCommandOutput.js +2 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -33,17 +33,19 @@ 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 │ 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.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
│
|
|
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. Found in: │
|
|
42
|
+
│ │ │ │ │ - index.js │
|
|
43
|
+
├─────────────────────────────────────────┼───────────┼────────────┼────────────┼────────────────────┤
|
|
44
|
+
│ fn-with-aws-sdk-in-package-json-deps │ us-east-2 │ nodejs24.x │ >=2.0.0 │ Yes. Found in: │
|
|
45
|
+
│ │ │ │ │ - index.mjs │
|
|
46
|
+
├─────────────────────────────────────────┼───────────┼────────────┼────────────┼────────────────────┤
|
|
47
|
+
│ fn-without-aws-sdk-in-package-json-deps │ us-east-2 │ nodejs24.x │ >=2.0.0 │ No. │
|
|
48
|
+
└─────────────────────────────────────────┴───────────┴────────────┴────────────┴────────────────────┘
|
|
47
49
|
```
|
|
48
50
|
|
|
49
51
|
This script requires AWS Managed Policy [AWSLambda_ReadOnlyAccess][].
|
|
@@ -11,7 +11,8 @@ import { join } from "node:path";
|
|
|
11
11
|
*/
|
|
12
12
|
export const getLambdaFunctionContents = async (zipPath) => {
|
|
13
13
|
const zip = new StreamZip.async({ file: zipPath });
|
|
14
|
-
const
|
|
14
|
+
const codeMap = {};
|
|
15
|
+
const packageJsonMap = {};
|
|
15
16
|
const awsSdkPackageJsonMap = {};
|
|
16
17
|
let zipEntries = {};
|
|
17
18
|
try {
|
|
@@ -30,51 +31,38 @@ export const getLambdaFunctionContents = async (zipPath) => {
|
|
|
30
31
|
}
|
|
31
32
|
continue;
|
|
32
33
|
}
|
|
33
|
-
// Skip
|
|
34
|
-
if (!zipEntry.name.endsWith(PACKAGE_JSON))
|
|
35
|
-
continue;
|
|
36
|
-
// Skip if 'package.json' is not a file
|
|
34
|
+
// Skip if it is not a file
|
|
37
35
|
if (!zipEntry.isFile)
|
|
38
36
|
continue;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (packageJsonFiles.length !== 0) {
|
|
52
|
-
await zip.close();
|
|
53
|
-
return {
|
|
54
|
-
packageJsonFiles,
|
|
55
|
-
...(Object.keys(awsSdkPackageJsonMap).length > 0 && { awsSdkPackageJsonMap }),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
for (const path of ["index.js", "index.mjs", "index.cjs"]) {
|
|
59
|
-
if (!zipEntries[path])
|
|
60
|
-
continue;
|
|
61
|
-
if (!zipEntries[path].isFile)
|
|
37
|
+
// Populate 'package.json' files.
|
|
38
|
+
if (zipEntry.name.endsWith(PACKAGE_JSON)) {
|
|
39
|
+
try {
|
|
40
|
+
const packageJsonContent = await zip.entryData(zipEntry.name);
|
|
41
|
+
packageJsonMap[zipEntry.name] = packageJsonContent.toString();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Continue without adding package.json file, if entry data can't be read.
|
|
45
|
+
// ToDo: add warning when logging is supported in future.
|
|
46
|
+
}
|
|
62
47
|
continue;
|
|
63
|
-
try {
|
|
64
|
-
const bundleContent = await zip.entryData(path);
|
|
65
|
-
await zip.close();
|
|
66
|
-
return {
|
|
67
|
-
bundleFile: {
|
|
68
|
-
path,
|
|
69
|
-
content: bundleContent.toString(),
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
48
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
49
|
+
// Populate JavaScript/TypeScript files.
|
|
50
|
+
if (zipEntry.name.match(/\.(js|ts|mjs|cjs)$/)) {
|
|
51
|
+
try {
|
|
52
|
+
const codeContent = await zip.entryData(zipEntry.name);
|
|
53
|
+
codeMap[zipEntry.name] = codeContent.toString();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Continue without adding code, if entry data can't be read.
|
|
57
|
+
// ToDo: add warning when logging is supported in future.
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
76
60
|
}
|
|
77
61
|
}
|
|
78
62
|
await zip.close();
|
|
79
|
-
return {
|
|
63
|
+
return {
|
|
64
|
+
codeMap,
|
|
65
|
+
...(Object.keys(packageJsonMap).length > 0 && { packageJsonMap }),
|
|
66
|
+
...(Object.keys(awsSdkPackageJsonMap).length > 0 && { awsSdkPackageJsonMap }),
|
|
67
|
+
};
|
|
80
68
|
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { satisfies, validate } from "compare-versions";
|
|
2
|
+
import { AWS_SDK, NODE_MODULES, PACKAGE_JSON } from "./constants.js";
|
|
2
3
|
import { downloadFile } from "./downloadFile.js";
|
|
3
4
|
import { getLambdaFunctionContents, } from "./getLambdaFunctionContents.js";
|
|
5
|
+
import { getPossibleHandlerFiles } from "./getPossibleHandlerFiles.js";
|
|
4
6
|
import { hasSdkV2InBundle } from "./hasSdkV2InBundle.js";
|
|
7
|
+
import { hasSdkV2InFile } from "./hasSdkV2InFile.js";
|
|
5
8
|
import { rm } from "node:fs/promises";
|
|
6
9
|
import { tmpdir } from "node:os";
|
|
7
10
|
import { dirname, join } from "node:path";
|
|
8
|
-
import { AWS_SDK, NODE_MODULES, PACKAGE_JSON } from "./constants.js";
|
|
9
11
|
/**
|
|
10
12
|
* Scans a Lambda function to detect AWS SDK for JavaScript v2 usage.
|
|
11
13
|
*
|
|
@@ -45,10 +47,38 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
|
|
|
45
47
|
finally {
|
|
46
48
|
await rm(zipPath, { force: true });
|
|
47
49
|
}
|
|
48
|
-
const {
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const { packageJsonMap, awsSdkPackageJsonMap, codeMap } = lambdaFunctionContents;
|
|
51
|
+
// Process handler as bundle file first.
|
|
52
|
+
const possibleHandlerFiles = getPossibleHandlerFiles(response.Configuration?.Handler ?? "index.handler");
|
|
53
|
+
for (const handlerFile of possibleHandlerFiles) {
|
|
54
|
+
if (handlerFile in codeMap) {
|
|
55
|
+
if (hasSdkV2InBundle(codeMap[handlerFile], sdkVersionRange)) {
|
|
56
|
+
output.ContainsAwsSdkJsV2 = true;
|
|
57
|
+
output.AwsSdkJsV2Locations = [handlerFile];
|
|
58
|
+
return output;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const filesWithJsSdkV2 = [];
|
|
63
|
+
// Search for JS SDK v2 occurrence in source code
|
|
64
|
+
for (const [filePath, fileContent] of Object.entries(codeMap)) {
|
|
65
|
+
try {
|
|
66
|
+
if (hasSdkV2InFile(filePath, fileContent)) {
|
|
67
|
+
filesWithJsSdkV2.push(filePath);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Skip files that fail to parse
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// JS SDK v2 not found in source code.
|
|
75
|
+
if (filesWithJsSdkV2.length === 0) {
|
|
76
|
+
output.ContainsAwsSdkJsV2 = false;
|
|
77
|
+
return output;
|
|
78
|
+
}
|
|
79
|
+
// Search for JS SDK v2 version from package.json
|
|
80
|
+
if (packageJsonMap && Object.keys(packageJsonMap).length > 0) {
|
|
81
|
+
for (const [packageJsonPath, packageJsonContent] of Object.entries(packageJsonMap)) {
|
|
52
82
|
try {
|
|
53
83
|
const packageJson = JSON.parse(packageJsonContent);
|
|
54
84
|
const dependencies = packageJson.dependencies || {};
|
|
@@ -86,7 +116,7 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
|
|
|
86
116
|
return output;
|
|
87
117
|
}
|
|
88
118
|
output.ContainsAwsSdkJsV2 = true;
|
|
89
|
-
output.
|
|
119
|
+
output.AwsSdkJsV2Locations = filesWithJsSdkV2;
|
|
90
120
|
return output;
|
|
91
121
|
}
|
|
92
122
|
}
|
|
@@ -98,22 +128,6 @@ export const getLambdaFunctionScanOutput = async (client, { functionName, region
|
|
|
98
128
|
}
|
|
99
129
|
}
|
|
100
130
|
}
|
|
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
|
-
}
|
|
116
|
-
}
|
|
117
131
|
// JS SDK v2 dependency/code not found.
|
|
118
132
|
output.ContainsAwsSdkJsV2 = false;
|
|
119
133
|
return output;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns possible file paths for a Lambda handler.
|
|
3
|
+
* @param handlerPath - Lambda handler path (e.g., "index.handler").
|
|
4
|
+
* @returns Array of possible file paths with js, mjs, cjs, ts extensions.
|
|
5
|
+
*/
|
|
6
|
+
export const getPossibleHandlerFiles = (handlerPath) => {
|
|
7
|
+
const [handlerFile] = handlerPath.split(".");
|
|
8
|
+
return [`${handlerFile}.js`, `${handlerFile}.mjs`, `${handlerFile}.cjs`, `${handlerFile}.ts`];
|
|
9
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { parseSync } from "oxc-parser";
|
|
2
|
+
const isAwsSdkV2 = (path) => path === "aws-sdk" || path.startsWith("aws-sdk/");
|
|
3
|
+
/**
|
|
4
|
+
* Recursively searches AST for JS SDK v2 require/import patterns.
|
|
5
|
+
* @param node - AST node to search.
|
|
6
|
+
* @returns true if JS SDK v2 require/import pattern is found.
|
|
7
|
+
*/
|
|
8
|
+
const hasAwsSdkV2InAst = (node) => {
|
|
9
|
+
if (!node || typeof node !== "object")
|
|
10
|
+
return false;
|
|
11
|
+
const n = node;
|
|
12
|
+
// Search for aws-sdk in require
|
|
13
|
+
if (n.type === "CallExpression" &&
|
|
14
|
+
n.callee?.name === "require" &&
|
|
15
|
+
isAwsSdkV2(n.arguments?.[0]?.value)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
// Search for aws-sdk in import equals
|
|
19
|
+
if (n.type === "TSImportEqualsDeclaration" &&
|
|
20
|
+
n.moduleReference?.type === "TSExternalModuleReference" &&
|
|
21
|
+
isAwsSdkV2(n.moduleReference?.expression?.value)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
// Search for aws-sdk in dynamic import
|
|
25
|
+
if (n.type === "ImportExpression" && isAwsSdkV2(n.source?.value ?? "")) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return Object.values(n).some((child) => Array.isArray(child) ? child.some(hasAwsSdkV2InAst) : hasAwsSdkV2InAst(child));
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a file contains AWS SDK for JavaScript v2 imports or requires.
|
|
32
|
+
* @param filePath - Path to the file (used for parser configuration).
|
|
33
|
+
* @param fileContent - Content of the file to analyze.
|
|
34
|
+
* @returns true if the file contains AWS SDK v2 usage.
|
|
35
|
+
*/
|
|
36
|
+
export const hasSdkV2InFile = (filePath, fileContent) => {
|
|
37
|
+
const { module, program } = parseSync(filePath, fileContent);
|
|
38
|
+
for (const { moduleRequest } of module.staticImports) {
|
|
39
|
+
if (isAwsSdkV2(moduleRequest.value))
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (hasAwsSdkV2InAst(program))
|
|
43
|
+
return true;
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
@@ -25,8 +25,8 @@ export const printLambdaCommandOutput = (output, outputType) => {
|
|
|
25
25
|
if (scanOutput.AwsSdkJsV2Error !== undefined) {
|
|
26
26
|
notes += ` ${scanOutput.AwsSdkJsV2Error}`;
|
|
27
27
|
}
|
|
28
|
-
if (scanOutput.
|
|
29
|
-
notes += ` ${scanOutput.
|
|
28
|
+
if (scanOutput.AwsSdkJsV2Locations !== undefined) {
|
|
29
|
+
notes += ` Found in:\n${scanOutput.AwsSdkJsV2Locations.map((location) => `- ${location}`).join("\n")}`;
|
|
30
30
|
}
|
|
31
31
|
table.push([
|
|
32
32
|
scanOutput.FunctionName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-sdk/find-v2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"commander": "^14.0.2",
|
|
12
12
|
"compare-versions": "^6.1.1",
|
|
13
13
|
"node-stream-zip": "^1.15.0",
|
|
14
|
+
"oxc-parser": "^0.107.0",
|
|
14
15
|
"p-limit": "^7.2.0"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|