feat(esmole): readonly request guard

This commit is contained in:
admin committed 2026-06-16 20:46:23 +05:00
1 parent a424963418
commit 7381db1189
2 files changed
+105

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
export class EsGuardError extends Error {
constructor(message: string) {
super(message)
this.name = 'EsGuardError'
}
}
// POST is allowed in readonly mode only when the path ends in one of these
// read-intent suffixes. Matched against the path with its query string and
// trailing slash stripped.
const READ_POST_SUFFIXES = [
'_search',
'_msearch',
'_count',
'_field_caps',
'_mapping',
'_search/scroll',
'_pit'
]
// DELETE is allowed in readonly mode only for read-session teardown.
const READ_DELETE_PATHS = ['_pit', '_search/scroll']
const normalize = (path: string): string => {
const noQuery = path.split('?')[0]
return noQuery.endsWith('/') ? noQuery.slice(0, -1) : noQuery
}
const endsWithSegment = (path: string, suffix: string): boolean =>
path === '/' + suffix || path.endsWith('/' + suffix)
export const assertReadAllowed = (method: string, path: string): void => {
const m = method.toUpperCase()
const p = normalize(path)
if (m === 'GET' || m === 'HEAD') {
return
}
if (m === 'POST') {
if (
p.includes('/_cat/') ||
p.endsWith('/_cat') ||
READ_POST_SUFFIXES.some((s) => endsWithSegment(p, s))
) {
return
}
throw new EsGuardError(
"readonly connection: POST to '" + p + "' is not a permitted read operation"
)
}
if (m === 'DELETE' && READ_DELETE_PATHS.some((s) => endsWithSegment(p, s))) {
return
}
throw new EsGuardError('readonly connection: ' + m + ' is not permitted')
}