Skip to content

Commit 44d1e98

Browse files
committed
kms-keyring client cache
1 parent e668fe4 commit 44d1e98

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

modules/kms-keyring/src/kms_client_supplier.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,45 @@ export function cacheClients<Client extends KMS> (
6969
const clientsCache: {[key: string]: Client|false} = {}
7070

7171
return (region: string) => {
72-
// undefined and false are both false ish, so check for the property
73-
if (!clientsCache.hasOwnProperty(region)) clientsCache[region] = getClient(region)
72+
// Do not cache until KMS has been responded in the given region
73+
if (!clientsCache.hasOwnProperty(region)) return deferCache(clientsCache, region, getClient(region))
7474
return clientsCache[region]
7575
}
7676
}
77+
78+
type KMSOperations = keyof KMS
79+
/* It is possible that a malicious user can attempt a local resource
80+
* DOS by sending ciphertext with a large number of spurious regions.
81+
* This will fill the cache with regions and exhaust resources.
82+
* To avoid this, a call succeeds in contacting KMS.
83+
* This does *not* mean that this call is successful,
84+
* only that the region is backed by a functional KMS service.
85+
*/
86+
function deferCache<Client extends KMS> (
87+
clientsCache: {[key: string]: Client|false},
88+
region: string,
89+
client: Client|false
90+
): Client|false {
91+
if (!client) {
92+
clientsCache[region] = false
93+
return false
94+
}
95+
const { encrypt, decrypt, generateDataKey } = client
96+
97+
return (<KMSOperations[]>['encrypt', 'decrypt', 'generateDataKey']).reduce(wrapOperation, client)
98+
99+
function wrapOperation (client: Client, name: KMSOperations): Client {
100+
type params = Parameters<KMS[typeof name]>
101+
type retValue = ReturnType<KMS[typeof name]>
102+
const original = client[name]
103+
client[name] = function (...args: params): retValue {
104+
// @ts-ignore (there should be a TypeScript solution for this)
105+
return original.apply(client, args)
106+
.then((response: any) => {
107+
clientsCache[region] = Object.assign(client, { encrypt, decrypt, generateDataKey })
108+
return response
109+
})
110+
}
111+
return client
112+
}
113+
}

0 commit comments

Comments
 (0)