Skip to content

Commit 9de2f9d

Browse files
Add multi-index comma-separated source FGAC bypass tests
Regression tests for authorization bypass where PPL multi-index queries (source = idx1, idx2) only evaluated permissions on the first index, allowing unauthorized access to subsequent indices. Tests cover: - Authorized index first, unauthorized second (the bypass vector) - Backtick-quoted variant of the same - Unauthorized index first (control: should be denied) - Both indices authorized (positive case) Signed-off-by: Finnegan Carroll <carrofin@amazon.com> Signed-off-by: Finn Carroll <carrofin@amazon.com>
1 parent 0b96cfc commit 9de2f9d

2 files changed

Lines changed: 66 additions & 5 deletions

File tree

integ-test/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ ext {
151151
'plugins.security.enable_snapshot_restore_privilege' : 'true',
152152
'plugins.security.check_snapshot_restore_write_privileges' : 'true',
153153
'plugins.security.restapi.roles_enabled' : '["all_access", "security_rest_api_access"]',
154-
'plugins.security.system_indices.enabled' : 'true'
154+
'plugins.security.system_indices.enabled' : 'true',
155+
'cluster.pluggable.dataformat' : 'composite'
155156
].forEach { name, value ->
156157
cluster.setting name, value
157158
}
@@ -502,11 +503,11 @@ testClusters.analyticsEngineSecurityIT {
502503
plugin(getJobSchedulerPlugin())
503504
plugin(getArrowBasePlugin())
504505
plugin(getArrowFlightRpcPlugin())
506+
plugin(getCompositeEnginePlugin())
505507
plugin(getAnalyticsEnginePlugin())
506508
plugin(getAnalyticsBackendLucenePlugin())
507509
plugin(getAnalyticsBackendDatafusionPlugin())
508510
plugin(getParquetDataFormatPlugin())
509-
plugin(getCompositeEnginePlugin())
510511
plugin ":opensearch-sql-plugin"
511512
// Arrow Flight / streaming transport requirements
512513
jvmArgs '--add-opens=java.base/java.nio=ALL-UNNAMED'

integ-test/src/test/java/org/opensearch/sql/security/AnalyticsEngineSecurityIT.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ private void createCompositeIndex(String index) throws IOException {
122122
{
123123
"settings": {
124124
"number_of_shards": 1,
125-
"number_of_replicas": 0,
126-
"index.pluggable.dataformat.enabled": true,
127-
"index.pluggable.dataformat": "composite"
125+
"number_of_replicas": 0
128126
}
129127
}
130128
""");
@@ -393,6 +391,68 @@ public void testPPLQueryWithWildcardIndexPartialAccessDenied() throws IOExceptio
393391
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
394392
}
395393

394+
// --- Multi-index comma-separated source tests (CVE: authorization bypass) ---
395+
396+
@Test
397+
public void testPPLMultiIndexDeniedWhenSecondIndexUnauthorized() throws IOException {
398+
// ALLOWED_USER has access to TEST_INDEX but NOT FORBIDDEN_INDEX.
399+
// A comma-separated source listing an authorized index first followed by an unauthorized
400+
// index must be denied. Regression: previously only the first index was checked.
401+
ResponseException e =
402+
assertThrows(
403+
ResponseException.class,
404+
() ->
405+
executePPLAsUser(
406+
"source = " + TEST_INDEX + ", " + FORBIDDEN_INDEX + " | fields name, age",
407+
ALLOWED_USER));
408+
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
409+
}
410+
411+
@Test
412+
public void testPPLMultiIndexDeniedWithBackticksAuthorizedFirst() throws IOException {
413+
// Same bypass vector using backtick-quoted index names.
414+
ResponseException e =
415+
assertThrows(
416+
ResponseException.class,
417+
() ->
418+
executePPLAsUser(
419+
"source = `" + TEST_INDEX + "`, `" + FORBIDDEN_INDEX + "` | fields name, age",
420+
ALLOWED_USER));
421+
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
422+
}
423+
424+
@Test
425+
public void testPPLMultiIndexDeniedWithUnauthorizedFirst() throws IOException {
426+
// Unauthorized index listed first — should also be denied.
427+
ResponseException e =
428+
assertThrows(
429+
ResponseException.class,
430+
() ->
431+
executePPLAsUser(
432+
"source = " + FORBIDDEN_INDEX + ", " + TEST_INDEX + " | fields name, age",
433+
ALLOWED_USER));
434+
assertEquals(403, e.getResponse().getStatusLine().getStatusCode());
435+
}
436+
437+
@Test
438+
public void testPPLMultiIndexAllowedWhenAllAuthorized() throws IOException {
439+
// ALLOWED_USER has access to TEST_INDEX. TEST_INDEX_2 is also covered by the
440+
// wildcard user's pattern but let's use allowed_user with both permitted indices.
441+
// Use WILDCARD_USER who has "analytics_security*" covering both TEST_INDEX and TEST_INDEX_2.
442+
try {
443+
JSONObject result =
444+
executePPLAsUser(
445+
"source = " + TEST_INDEX + ", " + TEST_INDEX_2 + " | fields name, age",
446+
WILDCARD_USER);
447+
assertTrue("Expected datarows in response", result.has("datarows"));
448+
} catch (ResponseException e) {
449+
assertNotEquals(
450+
"Expected auth to pass (not 403) when all indices are authorized",
451+
403,
452+
e.getResponse().getStatusLine().getStatusCode());
453+
}
454+
}
455+
396456
@Test
397457
public void testSQLQueryAllowedForAuthorizedUser() throws IOException {
398458
try {

0 commit comments

Comments
 (0)