Copilot commented on code in PR #13708:
URL: https://github.com/apache/trafficserver/pull/13708#discussion_r4266135911


##########
tests/gold_tests/pluginTest/access_control/access_control.test.py:
##########
@@ -59,16 +59,27 @@ def setupTS(self):
 
         self.ts.Setup.Copy("etc/hmac_keys.txt")
         self.ts.Disk.remap_config.AddLines(
-            {
+            [
+                f'''map /scoped/ 
https://127.0.0.1:{self.server.Variables.https_port}/scoped/ \
+    @plugin=access_control.so \
+    @pparam=--symmetric-keys-map={Test.RunDirectory}/hmac_keys.txt \
+    @pparam=--check-cookie=TokenCookie \
+    @pparam=--reject-invalid-token-requests=true \
+    @pparam=--enforce-scope=true \
+    @pparam=--invalid-scope-status-code=403 \
+    @pparam=--extract-subject-to-header=TokenSubject \
+    @pparam=--extract-tokenid-to-header=@TokenId \
+    @pparam=--extract-status-to-header=@TokenStatus \
+    @pparam=--token-response-header=TokenRespHdr''',
                 f'''map / 
https://127.0.0.1:{self.server.Variables.https_port}/ \
     @plugin=access_control.so \
     @pparam=--symmetric-keys-map={Test.RunDirectory}/hmac_keys.txt \
     @pparam=--check-cookie=TokenCookie \
-    @pparam=--extract-subject-to-header=@TokenSubject \
+    @pparam=--extract-subject-to-header=TokenSubject \
     @pparam=--extract-tokenid-to-header=@TokenId \
     @pparam=--extract-status-to-header=@TokenStatus \
-    @pparam=--token-response-header=TokenRespHdr'''
-            })
+    @pparam=--token-response-header=TokenRespHdr''',
+            ])

Review Comment:
   Gold session #5 in `access_control.replay.yaml` expects an out-of-scope 
request to *suppress* `TokenSubject` on the `/` remap, but this remap does not 
enable `--enforce-scope` (default is false). As written, scope will be ignored 
on `/`, and the subject header will still be extracted for valid tokens. To 
make the test and configuration consistent, enable `--enforce-scope=true` on 
the `/` remap as well (while keeping `--reject-invalid-token-requests` 
unset/false to preserve the non-rejecting behavior).



##########
plugins/experimental/access_control/plugin.cc:
##########
@@ -529,12 +529,31 @@ enforceAccessControl(TSHttpTxn txnp, TSRemapRequestInfo 
*rri, AccessControlConfi
           remapStatus =
             handleInvalidToken(txnp, data, reject, 
accessTokenStateToHttpStatus(data->_vaState, config), data->_vaState);
         } else {
-          /* Valid token, if configured extract the token subject to a header,
-           * only if we can trust it - token is valid to prevent using it by 
mistake */
-          if (!config->_extrSubHdrName.empty()) {
-            String sub(token->getSubject());
-            setHeader(rri->requestBufp, rri->requestHdrp, 
config->_extrSubHdrName.c_str(), config->_extrSubHdrName.size(),
-                      sub.c_str(), sub.size());
+          bool inScope = true;
+          if (config->_enforceScope) {
+            int                   pathLen = 0;
+            const char           *path    = TSUrlPathGet(rri->requestBufp, 
rri->requestUrl, &pathLen);
+            StringView            reqPath(path ? path : "", pathLen);
+            ScopeValidationResult scopeResult = validateScopeDetailed(reqPath, 
token->getScope());
+            if (scopeResult != ScopeValidationResult::IN_SCOPE) {
+              inScope = false;
+              if (scopeResult == ScopeValidationResult::INVALID_SCOPE) {
+                data->_vaState = INVALID_SCOPE;
+              } else {
+                data->_vaState = OUT_OF_SCOPE;
+              }
+              remapStatus =
+                handleInvalidToken(txnp, data, reject, 
accessTokenStateToHttpStatus(data->_vaState, config), data->_vaState);
+            }
+          }

Review Comment:
   `validateScopeDetailed()` distinguishes `INVALID_REQUEST_PATH` (e.g., 
traversal or invalid escapes) from `OUT_OF_SCOPE`, but this code maps 
everything except `INVALID_SCOPE` to `OUT_OF_SCOPE`. That collapses potentially 
malicious/invalid request paths into the same bucket as simple authorization 
failures and forces the `--invalid-scope-status-code` path. Consider mapping 
`INVALID_REQUEST_PATH` to an existing `AccessTokenStatus` that results in a 
400-class response (e.g., `INVALID_SYNTAX` / `_invalidRequest`), or introduce a 
dedicated `AccessTokenStatus` for invalid request paths so operators can 
distinguish bad input from authorization failures.



##########
plugins/experimental/access_control/access_control.cc:
##########
@@ -23,12 +23,104 @@
 
 #include <iostream>
 #include <string>
+#include <charconv>
 
 #include "access_control.h"
 
 size_t calcMessageDigest(const StringView hf, const char *secret, const char 
*message, size_t messageLen, char *buffer, size_t len);
 const char *getSecretMap(const StringMap &map, const StringView &key, size_t 
&secretSize);
 
+static bool
+normalizePath(StringView path, String &normalized)
+{
+  normalized.clear();
+  if (path.empty()) {
+    normalized = "/";
+    return true;
+  }
+
+  normalized.reserve(path.size() + 1);
+  size_t i = 0;
+
+  while (i < path.size()) {
+    while (i < path.size() && path[i] == '/') {
+      ++i;
+    }
+    if (i >= path.size()) {
+      break;
+    }
+
+    size_t seg_start = normalized.size();
+    normalized.push_back('/');
+
+    while (i < path.size() && path[i] != '/') {
+      if (path[i] == '\\') {
+        return false;
+      }
+      if (path[i] == '%') {
+        unsigned int val = 0;
+        if (i + 2 >= path.size()) {
+          return false;
+        }
+        auto [end, ec] = std::from_chars(path.data() + i + 1, path.data() + i 
+ 3, val, 16);
+        if (end == path.data() + i + 3 && ec == std::errc{} && val <= 0xFF) {

Review Comment:
   `std::errc` is specified in `<system_error>`. Relying on it being 
transitively included via `<charconv>` is not guaranteed across standard 
library implementations. Please add an explicit `#include <system_error>` (or 
adjust the code to avoid referencing `std::errc` directly) to improve 
portability.



##########
plugins/experimental/access_control/plugin.cc:
##########
@@ -529,12 +529,31 @@ enforceAccessControl(TSHttpTxn txnp, TSRemapRequestInfo 
*rri, AccessControlConfi
           remapStatus =
             handleInvalidToken(txnp, data, reject, 
accessTokenStateToHttpStatus(data->_vaState, config), data->_vaState);
         } else {
-          /* Valid token, if configured extract the token subject to a header,
-           * only if we can trust it - token is valid to prevent using it by 
mistake */
-          if (!config->_extrSubHdrName.empty()) {
-            String sub(token->getSubject());
-            setHeader(rri->requestBufp, rri->requestHdrp, 
config->_extrSubHdrName.c_str(), config->_extrSubHdrName.size(),
-                      sub.c_str(), sub.size());
+          bool inScope = true;
+          if (config->_enforceScope) {
+            int                   pathLen = 0;
+            const char           *path    = TSUrlPathGet(rri->requestBufp, 
rri->requestUrl, &pathLen);
+            StringView            reqPath(path ? path : "", pathLen);
+            ScopeValidationResult scopeResult = validateScopeDetailed(reqPath, 
token->getScope());

Review Comment:
   `validateScopeDetailed()` currently normalizes (and percent-decodes) both 
the request path and the token scope, which means the scope normalization work 
is repeated on every transaction even though the token scope is constant. If 
scope enforcement is expected to be commonly enabled, consider 
normalizing/caching the scope once at token parse time (e.g., storing a 
normalized scope string in the token instance) and only normalizing the request 
path per-request. This reduces per-request allocations and CPU for long-lived 
tokens.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to