@exabugs/dynamodb-client 1.13.1 → 1.14.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/CHANGELOG.md +21 -0
- package/README.md +5 -15
- package/bin/dynamodb-access-check.js +6 -0
- package/dist/server/handler.cjs +3 -3
- package/dist/server/handler.cjs.map +1 -1
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +2 -2
- package/dist/server/media-process-handler.cjs +2 -2
- package/dist/server/operations/parameterConverter.js +3 -3
- package/dist/server/operations/parameterConverter.js.map +1 -1
- package/dist/tools/dynamodb-access-check/allowlist.d.ts +39 -0
- package/dist/tools/dynamodb-access-check/allowlist.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/allowlist.js +34 -0
- package/dist/tools/dynamodb-access-check/allowlist.js.map +1 -0
- package/dist/tools/dynamodb-access-check/check.d.ts +24 -0
- package/dist/tools/dynamodb-access-check/check.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/check.js +40 -0
- package/dist/tools/dynamodb-access-check/check.js.map +1 -0
- package/dist/tools/dynamodb-access-check/cli.d.ts +10 -0
- package/dist/tools/dynamodb-access-check/cli.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/cli.js +96 -0
- package/dist/tools/dynamodb-access-check/cli.js.map +1 -0
- package/dist/tools/dynamodb-access-check/forbidden.d.ts +15 -0
- package/dist/tools/dynamodb-access-check/forbidden.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/forbidden.js +34 -0
- package/dist/tools/dynamodb-access-check/forbidden.js.map +1 -0
- package/dist/tools/dynamodb-access-check/glob.d.ts +8 -0
- package/dist/tools/dynamodb-access-check/glob.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/glob.js +37 -0
- package/dist/tools/dynamodb-access-check/glob.js.map +1 -0
- package/dist/tools/dynamodb-access-check/index.d.ts +5 -0
- package/dist/tools/dynamodb-access-check/index.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/index.js +4 -0
- package/dist/tools/dynamodb-access-check/index.js.map +1 -0
- package/dist/tools/dynamodb-access-check/run.d.ts +2 -0
- package/dist/tools/dynamodb-access-check/run.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/run.js +3 -0
- package/dist/tools/dynamodb-access-check/run.js.map +1 -0
- package/dist/tools/dynamodb-access-check/scan.d.ts +25 -0
- package/dist/tools/dynamodb-access-check/scan.d.ts.map +1 -0
- package/dist/tools/dynamodb-access-check/scan.js +151 -0
- package/dist/tools/dynamodb-access-check/scan.js.map +1 -0
- package/dist/tools/eslint/index.d.ts +28 -0
- package/dist/tools/eslint/index.d.ts.map +1 -0
- package/dist/tools/eslint/index.js +34 -0
- package/dist/tools/eslint/index.js.map +1 -0
- package/docs/DYNAMODB_ACCESS.md +152 -0
- package/package.json +11 -11
- package/bin/dynamodb-mcp.js +0 -12
- package/dist/mcp/adapter.d.ts +0 -36
- package/dist/mcp/adapter.d.ts.map +0 -1
- package/dist/mcp/adapter.js +0 -213
- package/dist/mcp/adapter.js.map +0 -1
- package/dist/mcp/cli.d.ts +0 -2
- package/dist/mcp/cli.d.ts.map +0 -1
- package/dist/mcp/cli.js +0 -122
- package/dist/mcp/cli.js.map +0 -1
- package/dist/mcp/index.d.ts +0 -7
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/index.js +0 -6
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -18
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -127
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/tools/deleteMany.d.ts +0 -12
- package/dist/mcp/tools/deleteMany.d.ts.map +0 -1
- package/dist/mcp/tools/deleteMany.js +0 -24
- package/dist/mcp/tools/deleteMany.js.map +0 -1
- package/dist/mcp/tools/deleteOne.d.ts +0 -12
- package/dist/mcp/tools/deleteOne.d.ts.map +0 -1
- package/dist/mcp/tools/deleteOne.js +0 -24
- package/dist/mcp/tools/deleteOne.js.map +0 -1
- package/dist/mcp/tools/find.d.ts +0 -12
- package/dist/mcp/tools/find.d.ts.map +0 -1
- package/dist/mcp/tools/find.js +0 -56
- package/dist/mcp/tools/find.js.map +0 -1
- package/dist/mcp/tools/findMany.d.ts +0 -12
- package/dist/mcp/tools/findMany.d.ts.map +0 -1
- package/dist/mcp/tools/findMany.js +0 -28
- package/dist/mcp/tools/findMany.js.map +0 -1
- package/dist/mcp/tools/findManyReference.d.ts +0 -12
- package/dist/mcp/tools/findManyReference.d.ts.map +0 -1
- package/dist/mcp/tools/findManyReference.js +0 -32
- package/dist/mcp/tools/findManyReference.js.map +0 -1
- package/dist/mcp/tools/findOne.d.ts +0 -12
- package/dist/mcp/tools/findOne.d.ts.map +0 -1
- package/dist/mcp/tools/findOne.js +0 -24
- package/dist/mcp/tools/findOne.js.map +0 -1
- package/dist/mcp/tools/generated.d.ts +0 -11
- package/dist/mcp/tools/generated.d.ts.map +0 -1
- package/dist/mcp/tools/generated.js +0 -171
- package/dist/mcp/tools/generated.js.map +0 -1
- package/dist/mcp/tools/index.d.ts +0 -13
- package/dist/mcp/tools/index.d.ts.map +0 -1
- package/dist/mcp/tools/index.js +0 -29
- package/dist/mcp/tools/index.js.map +0 -1
- package/dist/mcp/tools/insertMany.d.ts +0 -12
- package/dist/mcp/tools/insertMany.d.ts.map +0 -1
- package/dist/mcp/tools/insertMany.js +0 -28
- package/dist/mcp/tools/insertMany.js.map +0 -1
- package/dist/mcp/tools/insertOne.d.ts +0 -12
- package/dist/mcp/tools/insertOne.d.ts.map +0 -1
- package/dist/mcp/tools/insertOne.js +0 -24
- package/dist/mcp/tools/insertOne.js.map +0 -1
- package/dist/mcp/tools/updateMany.d.ts +0 -12
- package/dist/mcp/tools/updateMany.d.ts.map +0 -1
- package/dist/mcp/tools/updateMany.js +0 -28
- package/dist/mcp/tools/updateMany.js.map +0 -1
- package/dist/mcp/tools/updateOne.d.ts +0 -12
- package/dist/mcp/tools/updateOne.d.ts.map +0 -1
- package/dist/mcp/tools/updateOne.js +0 -38
- package/dist/mcp/tools/updateOne.js.map +0 -1
- package/dist/mcp/types.d.ts +0 -50
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/mcp/types.js +0 -49
- package/dist/mcp/types.js.map +0 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { AWS_V2_DYNAMODB_LABEL, isForbiddenSpecifier } from './forbidden.js';
|
|
4
|
+
import { matchesAny } from './glob.js';
|
|
5
|
+
/** 常に走査しないディレクトリ */
|
|
6
|
+
export const DEFAULT_IGNORED_DIRS = new Set([
|
|
7
|
+
'node_modules',
|
|
8
|
+
'dist',
|
|
9
|
+
'build',
|
|
10
|
+
'coverage',
|
|
11
|
+
'.git',
|
|
12
|
+
'.terraform',
|
|
13
|
+
'.expo',
|
|
14
|
+
'.next',
|
|
15
|
+
'cdk.out',
|
|
16
|
+
]);
|
|
17
|
+
const toPosix = (p) => p.split(sep).join('/');
|
|
18
|
+
/** dir 配下のファイルを集める(root からの相対パスを返す)。`ignore` の glob に一致するものは除く */
|
|
19
|
+
export function walkFiles(root, dirs, accept, ignore) {
|
|
20
|
+
const out = [];
|
|
21
|
+
const visit = (abs) => {
|
|
22
|
+
let names;
|
|
23
|
+
try {
|
|
24
|
+
names = readdirSync(abs);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
if (DEFAULT_IGNORED_DIRS.has(name))
|
|
31
|
+
continue;
|
|
32
|
+
const p = join(abs, name);
|
|
33
|
+
const rel = toPosix(relative(root, p));
|
|
34
|
+
if (matchesAny(ignore, rel))
|
|
35
|
+
continue;
|
|
36
|
+
if (statSync(p).isDirectory())
|
|
37
|
+
visit(p);
|
|
38
|
+
else if (accept(name))
|
|
39
|
+
out.push(rel);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
for (const d of dirs)
|
|
43
|
+
visit(resolve(root, d));
|
|
44
|
+
return [...new Set(out)].sort();
|
|
45
|
+
}
|
|
46
|
+
const lineOf = (text, index) => text.slice(0, index).split('\n').length;
|
|
47
|
+
const SOURCE_FILE = /\.(?:ts|tsx|js|jsx|mjs|cjs)$/;
|
|
48
|
+
const IMPORT_RE = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s+|\brequire\s*\(\s*)(['"])([^'"\n]+)\1/g;
|
|
49
|
+
const AWS_V2_RE = /\bAWS\.DynamoDB\b/g;
|
|
50
|
+
/** ソースから、禁止された import(と aws-sdk v2 の DynamoDB クライアント)を検出する */
|
|
51
|
+
export function scanSources(root, dirs, ignore) {
|
|
52
|
+
const findings = [];
|
|
53
|
+
const files = walkFiles(root, dirs, (n) => SOURCE_FILE.test(n) && !n.endsWith('.d.ts'), ignore);
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
const text = readFileSync(join(root, file), 'utf8');
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
const add = (detail, index) => {
|
|
58
|
+
if (seen.has(detail))
|
|
59
|
+
return; // 同じファイルの同じ違反は最初の行だけ
|
|
60
|
+
seen.add(detail);
|
|
61
|
+
findings.push({ file, line: lineOf(text, index), detail });
|
|
62
|
+
};
|
|
63
|
+
for (const m of text.matchAll(IMPORT_RE)) {
|
|
64
|
+
if (isForbiddenSpecifier(m[2]))
|
|
65
|
+
add(m[2], m.index ?? 0);
|
|
66
|
+
}
|
|
67
|
+
for (const m of text.matchAll(AWS_V2_RE))
|
|
68
|
+
add(AWS_V2_DYNAMODB_LABEL, m.index ?? 0);
|
|
69
|
+
}
|
|
70
|
+
return { findings, scanned: files.length };
|
|
71
|
+
}
|
|
72
|
+
/** 行頭のコメント行を、行数を保ったまま空にする */
|
|
73
|
+
const stripCommentLines = (text) => text.replace(/^[ \t]*(?:#|\/\/).*$/gm, '');
|
|
74
|
+
/** `resource|data "T" "N" {` ブロックの範囲を、波括弧の対応(文字列内は無視)で切り出す */
|
|
75
|
+
function tfBlocks(text) {
|
|
76
|
+
const blocks = [];
|
|
77
|
+
const re = /^(resource|data)\s+"([^"]+)"\s+"([^"]+)"\s*\{/gm;
|
|
78
|
+
let m;
|
|
79
|
+
while ((m = re.exec(text))) {
|
|
80
|
+
let depth = 1;
|
|
81
|
+
let inStr = false;
|
|
82
|
+
let i = re.lastIndex;
|
|
83
|
+
for (; i < text.length && depth > 0; i++) {
|
|
84
|
+
const c = text[i];
|
|
85
|
+
if (inStr) {
|
|
86
|
+
if (c === '\\')
|
|
87
|
+
i++;
|
|
88
|
+
else if (c === '"')
|
|
89
|
+
inStr = false;
|
|
90
|
+
}
|
|
91
|
+
else if (c === '"')
|
|
92
|
+
inStr = true;
|
|
93
|
+
else if (c === '{')
|
|
94
|
+
depth++;
|
|
95
|
+
else if (c === '}')
|
|
96
|
+
depth--;
|
|
97
|
+
}
|
|
98
|
+
blocks.push({
|
|
99
|
+
name: `${m[1] === 'data' ? 'data.' : ''}${m[2]}.${m[3]}`,
|
|
100
|
+
start: m.index,
|
|
101
|
+
end: i,
|
|
102
|
+
});
|
|
103
|
+
re.lastIndex = i;
|
|
104
|
+
}
|
|
105
|
+
return blocks;
|
|
106
|
+
}
|
|
107
|
+
/** ARN の `aws:dynamodb:` を除くため、直前が識別子・`:` でないものだけ */
|
|
108
|
+
const TF_ACTION_RE = /(?<![A-Za-z0-9:_-])dynamodb:[A-Za-z*]+|\bAmazonDynamoDB[A-Za-z]*/g;
|
|
109
|
+
/** 名前のないブロック(locals・module など)の検出に使う名前 */
|
|
110
|
+
export const TF_TOP_LEVEL = '(top-level)';
|
|
111
|
+
/** Terraform から、DynamoDB の権限(`dynamodb:*`、AWS 管理ポリシー)を持つブロックを検出する */
|
|
112
|
+
export function scanTerraform(root, dirs, ignore) {
|
|
113
|
+
const findings = [];
|
|
114
|
+
const files = walkFiles(root, dirs, (n) => n.endsWith('.tf'), ignore);
|
|
115
|
+
for (const file of files) {
|
|
116
|
+
const text = stripCommentLines(readFileSync(join(root, file), 'utf8'));
|
|
117
|
+
const blocks = tfBlocks(text);
|
|
118
|
+
for (const b of blocks) {
|
|
119
|
+
const actions = [...new Set(text.slice(b.start, b.end).match(TF_ACTION_RE) ?? [])];
|
|
120
|
+
if (actions.length > 0) {
|
|
121
|
+
findings.push({
|
|
122
|
+
file,
|
|
123
|
+
line: lineOf(text, b.start),
|
|
124
|
+
detail: actions.join(', '),
|
|
125
|
+
terraform: b.name,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// resource / data 以外(locals・module など)に書かれた権限
|
|
130
|
+
let rest = text;
|
|
131
|
+
for (const b of [...blocks].reverse()) {
|
|
132
|
+
rest =
|
|
133
|
+
rest.slice(0, b.start) +
|
|
134
|
+
rest.slice(b.start, b.end).replace(/[^\n]/g, ' ') +
|
|
135
|
+
rest.slice(b.end);
|
|
136
|
+
}
|
|
137
|
+
const first = TF_ACTION_RE.exec(rest);
|
|
138
|
+
TF_ACTION_RE.lastIndex = 0;
|
|
139
|
+
if (first) {
|
|
140
|
+
const actions = [...new Set(rest.match(TF_ACTION_RE) ?? [])];
|
|
141
|
+
findings.push({
|
|
142
|
+
file,
|
|
143
|
+
line: lineOf(rest, first.index),
|
|
144
|
+
detail: actions.join(', '),
|
|
145
|
+
terraform: TF_TOP_LEVEL,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { findings, scanned: files.length };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-access-check/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface DynamoDbGuardOptions {
|
|
2
|
+
/** 許可リストの JSON(CLI `dynamodb-access-check` と同じもの。相対パスはカレントディレクトリから) */
|
|
3
|
+
allowlistPath: string;
|
|
4
|
+
/** 禁止を適用するファイル(既定: すべての JS / TS) */
|
|
5
|
+
files?: string[];
|
|
6
|
+
/** エラーメッセージ(既定の文言を差し替える) */
|
|
7
|
+
message?: string;
|
|
8
|
+
/** 利用側固有の禁止パターン(`no-restricted-imports` の `patterns`。同じルールに合流させる) */
|
|
9
|
+
patterns?: {
|
|
10
|
+
group: string[];
|
|
11
|
+
message?: string;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
/** ESLint の flat config のオブジェクト(ESLint の型に依存しないため、必要な形だけ) */
|
|
15
|
+
export interface FlatConfigObject {
|
|
16
|
+
files?: string[];
|
|
17
|
+
ignores?: string[];
|
|
18
|
+
rules: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* DynamoDB への直接アクセスを禁止する flat config を返す。
|
|
22
|
+
* 許可リストの JSON から、許可するファイルの overrides を生成する(二重管理にしない)。
|
|
23
|
+
* ESLint 自体は import しない(設定オブジェクトを返すだけ)。
|
|
24
|
+
*
|
|
25
|
+
* 許可リストの `file` は、ESLint を実行するディレクトリ(設定ファイルのあるディレクトリ)からの相対パス。
|
|
26
|
+
*/
|
|
27
|
+
export declare function createDynamoDbGuardConfig(options: DynamoDbGuardOptions): FlatConfigObject[];
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/eslint/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACpD;AAED,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAMD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,GAAG,gBAAgB,EAAE,CAuB3F"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { FORBIDDEN_IMPORT_NAMES } from '../dynamodb-access-check/forbidden.js';
|
|
4
|
+
const DEFAULT_FILES = ['**/*.{ts,tsx,js,jsx,mjs,cjs}'];
|
|
5
|
+
const DEFAULT_MESSAGE = 'DynamoDB への直接アクセスは禁止(Records 経由に限る)。DynamoClient を使う。例外は許可リストに理由つきで足す。';
|
|
6
|
+
/**
|
|
7
|
+
* DynamoDB への直接アクセスを禁止する flat config を返す。
|
|
8
|
+
* 許可リストの JSON から、許可するファイルの overrides を生成する(二重管理にしない)。
|
|
9
|
+
* ESLint 自体は import しない(設定オブジェクトを返すだけ)。
|
|
10
|
+
*
|
|
11
|
+
* 許可リストの `file` は、ESLint を実行するディレクトリ(設定ファイルのあるディレクトリ)からの相対パス。
|
|
12
|
+
*/
|
|
13
|
+
export function createDynamoDbGuardConfig(options) {
|
|
14
|
+
const message = options.message ?? DEFAULT_MESSAGE;
|
|
15
|
+
const raw = JSON.parse(readFileSync(resolve(options.allowlistPath), 'utf8'));
|
|
16
|
+
const codeEntries = (raw.entries ?? []).filter((e) => e.terraform === undefined);
|
|
17
|
+
const restriction = {
|
|
18
|
+
paths: FORBIDDEN_IMPORT_NAMES.map((name) => ({ name, message })),
|
|
19
|
+
};
|
|
20
|
+
if (options.patterns && options.patterns.length > 0)
|
|
21
|
+
restriction.patterns = options.patterns;
|
|
22
|
+
const files = options.files ?? DEFAULT_FILES;
|
|
23
|
+
const off = { 'no-restricted-imports': 'off' };
|
|
24
|
+
// exclude のないエントリは 1 つにまとめ、あるものは個別に(ignores を付ける)
|
|
25
|
+
const plain = codeEntries.filter((e) => !e.exclude?.length).map((e) => e.file);
|
|
26
|
+
return [
|
|
27
|
+
{ files, rules: { 'no-restricted-imports': ['error', restriction] } },
|
|
28
|
+
...(plain.length > 0 ? [{ files: plain, rules: off }] : []),
|
|
29
|
+
...codeEntries
|
|
30
|
+
.filter((e) => e.exclude?.length)
|
|
31
|
+
.map((e) => ({ files: [e.file], ignores: e.exclude, rules: off })),
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAoB/E,MAAM,aAAa,GAAG,CAAC,8BAA8B,CAAC,CAAC;AACvD,MAAM,eAAe,GACnB,wEAAwE,CAAC;AAE3E;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA6B;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC;IACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,CAE1E,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IAEjF,MAAM,WAAW,GAA4B;QAC3C,KAAK,EAAE,sBAAsB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;KACjE,CAAC;IACF,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,WAAW,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAE7F,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;IAC7C,MAAM,GAAG,GAAG,EAAE,uBAAuB,EAAE,KAAK,EAAE,CAAC;IAC/C,kDAAkD;IAClD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/E,OAAO;QACL,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,uBAAuB,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE;QACrE,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,WAAW;aACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;KACrE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# DynamoDB へのアクセスのガードレール
|
|
2
|
+
|
|
3
|
+
## 原則
|
|
4
|
+
|
|
5
|
+
dynamodb-client を使った DynamoDB の操作は、**Records(サーバー)の 1 か所**で行う。クライアントは、通常の操作の API(`DynamoClient`)を呼ぶだけ。**例外を作らない。**
|
|
6
|
+
例外(Lambda が本体テーブルを直接読み書きするなど)は、監査ログ(ADR 0004)のような横断機能の穴になる。
|
|
7
|
+
|
|
8
|
+
## 検査
|
|
9
|
+
|
|
10
|
+
1 つの許可リスト(JSON)を、2 つの仕組みが読む。**検査の実装はこのライブラリが提供する**。利用側(asanowa・astro など)は、スクリプトをコピーしない。
|
|
11
|
+
|
|
12
|
+
| 対象 | 仕組み | 実行 |
|
|
13
|
+
| ---------------------- | ------------------------------------------------------------------------------ | ------------------------ |
|
|
14
|
+
| コード・IAM | CLI `dynamodb-access-check`(ソースの import と Terraform を走査) | CI・`npm run` / `make` |
|
|
15
|
+
| コード(エディタ・CI) | ESLint の共有設定 `@exabugs/dynamodb-client/eslint`(`no-restricted-imports`) | `eslint`(CI・エディタ) |
|
|
16
|
+
|
|
17
|
+
禁止するもの:
|
|
18
|
+
|
|
19
|
+
- `@aws-sdk/client-dynamodb`、`@aws-sdk/lib-dynamodb`、`@aws-sdk/util-dynamodb` の import(サブパスも)
|
|
20
|
+
- aws-sdk v2 の DynamoDB クライアント(`aws-sdk/clients/dynamodb` の import、`AWS` 名前空間の DynamoDB の利用)
|
|
21
|
+
- このライブラリの内部のうち DynamoDB に触れるもの: `@exabugs/dynamodb-client/server`、`/server/handler`、`/server/media-handler`、`/server/media-process-handler`、`/server/media-process-handler-dlq`(`server/media/*`、`server/media-sign` は DynamoDB に触れないので許可)
|
|
22
|
+
- Terraform の `dynamodb:*` の権限(`resource` / `data` ブロック、`locals` など。AWS 管理ポリシー `AmazonDynamoDB*` も)
|
|
23
|
+
|
|
24
|
+
このライブラリ自身は、Records の中身(`src/server/operations/**`)を `src/server/**` の外から import することも、ESLint の `patterns` で禁止している(利用側は不要)。
|
|
25
|
+
|
|
26
|
+
## 利用側の手順
|
|
27
|
+
|
|
28
|
+
### 1. インストール
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install --save-dev @exabugs/dynamodb-client # 既に依存していれば、版を上げるだけ
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`bin` に `dynamodb-access-check` が入る(`npx dynamodb-access-check`)。
|
|
35
|
+
|
|
36
|
+
### 2. 許可リストを書く
|
|
37
|
+
|
|
38
|
+
任意の場所に JSON を置く(例: `dynamodb-access-allowlist.json`)。**3 リポジトリ共通の形式**:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"entries": [
|
|
43
|
+
{
|
|
44
|
+
"file": "functions/pre-token-gen/src/index.ts",
|
|
45
|
+
"kind": "permanent",
|
|
46
|
+
"reason": "Cognito のトリガー。admins レコードの GetItem だけ"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"file": "infra/modules/device-cleanup/main.tf",
|
|
50
|
+
"terraform": "aws_iam_role_policy.device_cleanup_dynamodb",
|
|
51
|
+
"kind": "debt",
|
|
52
|
+
"reason": "本体テーブルへの Query / Update",
|
|
53
|
+
"todo": "DynamoClient 経由にして、この権限を削除する"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"notes": ["任意のメモ"]
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| フィールド | 必須 | 内容 |
|
|
61
|
+
| ------------ | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `file` | 必須 | `--root` からの相対パス。コードは glob 可(`**`・`*`・`?`)。Terraform は `.tf` のパス |
|
|
63
|
+
| `terraform` | Terraform のみ | resource / data の名前。`aws_iam_role_policy.x`、data は `data.aws_iam_policy_document.y`、`resource` / `data` 以外のブロック(`locals` など)は `(top-level)` |
|
|
64
|
+
| `kind` | 必須 | `permanent`(構造上必要な恒久の例外)または `debt`(期限つきの負債) |
|
|
65
|
+
| `reason` | 必須 | 理由 |
|
|
66
|
+
| `todo` | debt で必須 | 解消の道筋 |
|
|
67
|
+
| `exclude` | 任意 | `file` の glob から除くもの(ESLint でも同じ除外になる) |
|
|
68
|
+
| `line` | 任意 | 検出した行。ずれていても警告だけ |
|
|
69
|
+
| `eslintOnly` | 任意 | ESLint の独自の `patterns` だけが検出する例外。CLI の「検出されない」警告を出さない |
|
|
70
|
+
| `notes` | 任意 | メモ(検査には使わない) |
|
|
71
|
+
|
|
72
|
+
粒度は**ファイル単位**(import の指定子や IAM のアクション単位の許可はない)。
|
|
73
|
+
|
|
74
|
+
### 3. CLI
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx dynamodb-access-check --allowlist dynamodb-access-allowlist.json \
|
|
78
|
+
--src apps functions packages scripts --tf infra \
|
|
79
|
+
--ignore '**/__tests__/**' '**/*.test.*'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| 引数 | 内容 |
|
|
83
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| `--allowlist <path>` | 許可リストの JSON(必須) |
|
|
85
|
+
| `--root <dir>` | 許可リストの `file`・`--src`・`--tf`・`--ignore` の基準(既定: カレントディレクトリ) |
|
|
86
|
+
| `--src <dir>...` | 走査するソース(`.ts` `.tsx` `.js` `.jsx` `.mjs` `.cjs`。`.d.ts` は除く)。既定: `.` |
|
|
87
|
+
| `--tf <dir>...` | 走査する Terraform(`.tf`)。既定: `.` |
|
|
88
|
+
| `--ignore <glob>...` | 走査から除く glob(`--root` からの相対パス)。`node_modules`・`dist`・`build`・`coverage`・`.git`・`.terraform`・`.expo`・`.next`・`cdk.out` は常に除く |
|
|
89
|
+
|
|
90
|
+
設定ファイルはない(引数だけ)。値は複数並べられる(次の `--` まで)。
|
|
91
|
+
|
|
92
|
+
動作と終了コード:
|
|
93
|
+
|
|
94
|
+
- 許可リストに無い違反 → 失敗(終了コード 1。`NG:` にファイル・行・リソースを表示)。許可リストの書式の不正(`reason` なし、debt に `todo` なし)も失敗
|
|
95
|
+
- 許可リストにあるのに検出されない → 警告(`WARN:`。負債が消えたら、リストからも消す)。行番号のずれも警告だけ
|
|
96
|
+
- 引数・読み込みの誤り → 終了コード 2
|
|
97
|
+
- 負債は、毎回一覧に表示する
|
|
98
|
+
|
|
99
|
+
### 4. ESLint の設定に組み込む
|
|
100
|
+
|
|
101
|
+
`eslint.config.js`(flat config)で、同じ許可リストから設定を作る。ESLint 自体は import されない(設定オブジェクトを返すだけ)。
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
import { createDynamoDbGuardConfig } from '@exabugs/dynamodb-client/eslint';
|
|
105
|
+
|
|
106
|
+
export default [
|
|
107
|
+
// ...
|
|
108
|
+
...createDynamoDbGuardConfig({
|
|
109
|
+
allowlistPath: 'dynamodb-access-allowlist.json', // ESLint を実行するディレクトリからの相対パス
|
|
110
|
+
// files: ['src/**/*.ts'], // 禁止を適用するファイル(既定: すべての JS / TS)
|
|
111
|
+
// message: '...', // エラーメッセージ
|
|
112
|
+
// patterns: [{ group: ['../ops/**'], message: '...' }], // 固有の禁止(同じルールに合流する)
|
|
113
|
+
}),
|
|
114
|
+
];
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
返る設定: ① `no-restricted-imports: error`(禁止の import)、② 許可リストのコードのエントリ(`file`)で、ルールを `off` にする overrides(`exclude` は `ignores`)。Terraform のエントリは使わない。`file` は ESLint を実行するディレクトリからの相対パス。このライブラリ自身の `eslint.config.js` が使用例(`patterns` で固有の禁止を足している)。
|
|
118
|
+
|
|
119
|
+
### 5. CI に組み込む
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
- run: npx eslint . --max-warnings 0
|
|
123
|
+
- name: Check DynamoDB access guardrail
|
|
124
|
+
run: npx dynamodb-access-check --allowlist dynamodb-access-allowlist.json --src apps functions --tf infra
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
ESLint は、エディタでも違反をその場で示す。CLI は ESLint の対象外のファイル(運用スクリプトなど)と Terraform まで見る。
|
|
128
|
+
|
|
129
|
+
## 許可リストの種別と約束
|
|
130
|
+
|
|
131
|
+
- **恒久(permanent)**: 構造上必要な例外。このライブラリでは、Records の本体、Records が使う共有部品、別テーブル `notices`(`src/notify/store.ts`。内部用で、本体テーブルではない。理由は ADR 0005〔提案中〕)
|
|
132
|
+
- **負債(debt)**: 期限つき。消す作業が決まっている
|
|
133
|
+
|
|
134
|
+
運用の約束:
|
|
135
|
+
|
|
136
|
+
- **許可リストは減る一方。増やすには理由が要る。**
|
|
137
|
+
- 負債が消えたら、許可リストのエントリも消す。CLI は、リストにあるのに存在しないエントリを警告する
|
|
138
|
+
- 違反を直さずにこのガードレールを外さない
|
|
139
|
+
|
|
140
|
+
## このライブラリ自身
|
|
141
|
+
|
|
142
|
+
- 許可リスト: `scripts/dynamodb-access-allowlist.json`
|
|
143
|
+
- CLI: `npm run check:dynamodb-access`(CI のステップ)
|
|
144
|
+
- ESLint: `eslint.config.js` が `createDynamoDbGuardConfig` を使う(ビルド済みの `dist/` を読むため、`npm run lint` は先に `tsc` を実行する)
|
|
145
|
+
|
|
146
|
+
## 現状の負債
|
|
147
|
+
|
|
148
|
+
| 場所 | 内容 | 消す作業 |
|
|
149
|
+
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |
|
|
150
|
+
| `src/server/media/process-handler.ts`、`process-handler-dlq.ts` | Records の `handle*` を直接呼ぶ | media の Lambda を Records のクライアントにする(ADR 0005〔提案中〕) |
|
|
151
|
+
| `terraform/media/main.tf` の `process_handler_dynamodb`、`process_handler_dlq_dynamodb` | media の Lambda に本体テーブルの権限 | 同上 |
|
|
152
|
+
| `src/dynamodb.ts` | 本体と重複した未使用のクライアント生成(テストからのみ参照) | `src/dynamodb.ts` と `__tests__/dynamodb.test.ts` の削除 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exabugs/dynamodb-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "DynamoDB Single-Table Client SDK with MongoDB-like API, Shadow Records, and Lambda implementation for serverless applications",
|
|
5
5
|
"author": "exabugs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"bin": {
|
|
11
|
-
"dynamodb-
|
|
11
|
+
"dynamodb-access-check": "./bin/dynamodb-access-check.js"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"dynamodb",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"README.md",
|
|
43
43
|
"LICENSE",
|
|
44
44
|
"CHANGELOG.md",
|
|
45
|
-
"docs/NOTIFY.md"
|
|
45
|
+
"docs/NOTIFY.md",
|
|
46
|
+
"docs/DYNAMODB_ACCESS.md"
|
|
46
47
|
],
|
|
47
48
|
"exports": {
|
|
48
49
|
".": {
|
|
@@ -85,10 +86,6 @@
|
|
|
85
86
|
"import": "./dist/integrations/react-admin/index.js",
|
|
86
87
|
"types": "./dist/integrations/react-admin/index.d.ts"
|
|
87
88
|
},
|
|
88
|
-
"./mcp": {
|
|
89
|
-
"import": "./dist/mcp/index.js",
|
|
90
|
-
"types": "./dist/mcp/index.d.ts"
|
|
91
|
-
},
|
|
92
89
|
"./server/media/presign": {
|
|
93
90
|
"import": "./dist/server/media/presign.js",
|
|
94
91
|
"types": "./dist/server/media/presign.d.ts"
|
|
@@ -119,6 +116,10 @@
|
|
|
119
116
|
},
|
|
120
117
|
"./terraform": "./terraform/main.tf",
|
|
121
118
|
"./terraform/media": "./terraform/media/main.tf",
|
|
119
|
+
"./eslint": {
|
|
120
|
+
"import": "./dist/tools/eslint/index.js",
|
|
121
|
+
"types": "./dist/tools/eslint/index.d.ts"
|
|
122
|
+
},
|
|
122
123
|
"./notify": {
|
|
123
124
|
"import": "./dist/notify/index.js",
|
|
124
125
|
"types": "./dist/notify/index.d.ts"
|
|
@@ -139,7 +140,9 @@
|
|
|
139
140
|
"build:lambda": "node esbuild.config.js",
|
|
140
141
|
"build:sharp-layer": "bash scripts/build-sharp-layer.sh",
|
|
141
142
|
"clean": "rm -rf dist",
|
|
143
|
+
"prelint": "npm run build:client",
|
|
142
144
|
"lint": "eslint src --ext ts --max-warnings 0",
|
|
145
|
+
"check:dynamodb-access": "npm run build:client && node bin/dynamodb-access-check.js --allowlist scripts/dynamodb-access-allowlist.json --src src --tf terraform",
|
|
143
146
|
"format": "prettier --write .",
|
|
144
147
|
"format:check": "prettier --check .",
|
|
145
148
|
"test": "vitest run",
|
|
@@ -148,7 +151,6 @@
|
|
|
148
151
|
"test:watch": "vitest",
|
|
149
152
|
"test:unit": "vitest run --exclude='**/*integration*.test.ts'",
|
|
150
153
|
"analyze-functions": "node scripts/find-large-functions.js",
|
|
151
|
-
"generate-mcp-tools": "tsx scripts/generate-mcp-tools.ts",
|
|
152
154
|
"generate-types": "orval",
|
|
153
155
|
"prepublishOnly": "npm run build && npm run test:unit"
|
|
154
156
|
},
|
|
@@ -161,7 +163,6 @@
|
|
|
161
163
|
"@aws-sdk/credential-provider-node": "3.946.0",
|
|
162
164
|
"@aws-sdk/lib-dynamodb": "3.946.0",
|
|
163
165
|
"@aws-sdk/s3-presigned-post": "3.946.0",
|
|
164
|
-
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
165
166
|
"@smithy/protocol-http": "5.3.5",
|
|
166
167
|
"@smithy/signature-v4": "5.3.5",
|
|
167
168
|
"@smithy/util-utf8": "4.2.0",
|
|
@@ -189,8 +190,7 @@
|
|
|
189
190
|
"sharp": "^0.35.5",
|
|
190
191
|
"tsx": "^4.21.0",
|
|
191
192
|
"typescript": "^5.3.0",
|
|
192
|
-
"vitest": "^2.0.0"
|
|
193
|
-
"yaml": "^2.8.2"
|
|
193
|
+
"vitest": "^2.0.0"
|
|
194
194
|
},
|
|
195
195
|
"engines": {
|
|
196
196
|
"node": ">=18.0.0"
|
package/bin/dynamodb-mcp.js
DELETED
package/dist/mcp/adapter.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { type MCPServerConfig } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* MCPアダプター
|
|
4
|
-
* MCPツール呼び出しをLambda操作に変換する
|
|
5
|
-
*/
|
|
6
|
-
export declare class MCPAdapter {
|
|
7
|
-
constructor(config: MCPServerConfig);
|
|
8
|
-
/**
|
|
9
|
-
* MCPツールを実行
|
|
10
|
-
* @param toolName ツール名
|
|
11
|
-
* @param args ツール引数
|
|
12
|
-
* @returns 実行結果
|
|
13
|
-
*/
|
|
14
|
-
executeTool(toolName: string, args: Record<string, unknown>): Promise<unknown>;
|
|
15
|
-
/**
|
|
16
|
-
* エラーを処理してMCPErrorに変換
|
|
17
|
-
* @param error 元のエラー
|
|
18
|
-
* @param toolName ツール名
|
|
19
|
-
* @param requestId リクエストID
|
|
20
|
-
* @returns MCPError
|
|
21
|
-
*/
|
|
22
|
-
private handleError;
|
|
23
|
-
/**
|
|
24
|
-
* MCPツール名をAPIリクエストに変換
|
|
25
|
-
* @param toolName ツール名
|
|
26
|
-
* @param args ツール引数
|
|
27
|
-
* @returns APIリクエスト
|
|
28
|
-
*/
|
|
29
|
-
private convertToolToApiRequest;
|
|
30
|
-
/**
|
|
31
|
-
* リクエストIDを生成
|
|
32
|
-
* @returns リクエストID
|
|
33
|
-
*/
|
|
34
|
-
private generateRequestId;
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/mcp/adapter.ts"],"names":[],"mappings":"AASA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAU1E;;;GAGG;AACH,qBAAa,UAAU;gBACT,MAAM,EAAE,eAAe;IAyBnC;;;;;OAKG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAsCpF;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAgFnB;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAsE/B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAG1B"}
|