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


##########
plugins/experimental/access_control/access_control.cc:
##########
@@ -470,6 +574,36 @@ accessTokenStatusToString(const AccessTokenStatus &state)
   return s;
 }
 
+/**
+ * Validates the request path against the token scope using normalized segment 
boundaries.
+ */
+bool
+validateScope(StringView requestPath, StringView scope)

Review Comment:
   This is a second definition of `validateScope(StringView, StringView)`; the 
same function is already defined at lines 131-135. Because both definitions are 
in this translation unit (and `access_control.cc` is compiled by both the 
plugin and unit-test targets), this redefinition prevents the targets from 
compiling. Remove this stale duplicate and keep the implementation that 
delegates to `validateScopeDetailed`.



##########
plugins/experimental/access_control/access_control.cc:
##########
@@ -23,12 +23,117 @@
 
 #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, bool isScope = false)
+{
+  normalized.clear();
+  if (path.empty()) {
+    normalized = "/";
+    return true;
+  }
+
+  normalized.reserve(path.size() + 1);
+  std::vector<size_t> segment_starts;
+  size_t              i = 0;
+
+  while (i < path.size()) {
+    while (i < path.size() && (path[i] == '/' || path[i] == '\\')) {
+      ++i;
+    }
+    if (i >= path.size()) {
+      break;
+    }
+
+    size_t seg_start = normalized.size();
+    normalized.push_back('/');
+
+    while (i < path.size() && path[i] != '/' && path[i] != '\\') {
+      if (path[i] == '%') {
+        unsigned int val = 0;
+        if (i + 2 < path.size() && std::from_chars(path.data() + i + 1, 
path.data() + i + 3, val, 16).ec == std::errc{} &&
+            val <= 0xFF) {

Review Comment:
   The `from_chars` call only checks `ec`, not that both hex digits were 
consumed. A malformed escape such as `%2g` therefore parses as `0x2` and is 
silently normalized instead of being rejected, allowing invalid scope/request 
paths to participate in authorization matching. Require the returned pointer to 
equal the two-digit end pointer.



##########
plugins/experimental/access_control/plugin.cc:
##########
@@ -529,12 +529,28 @@ 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());
+          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) {
+            if (scopeResult == ScopeValidationResult::INVALID_SCOPE) {
+              data->_vaState = INVALID_SCOPE;
+            } else if (scopeResult == 
ScopeValidationResult::INVALID_REQUEST_PATH) {

Review Comment:
   The new enforcement behavior is only covered by direct `validateScope` unit 
tests; no test drives `enforceAccessControl()` through `TSUrlPathGet()`. Add a 
plugin-level test with a signed scoped token that verifies an out-of-scope 
request gets the configured status and does not receive the subject header, 
while an in-scope request does. Without this, regressions in the request-path 
plumbing, status mapping, or header suppression can pass the current suite.



##########
plugins/experimental/access_control/access_control.h:
##########
@@ -105,8 +105,27 @@ enum AccessTokenStatus {
   MAX,
 };
 
+enum class ScopeValidationResult {
+  IN_SCOPE,
+  OUT_OF_SCOPE,
+  INVALID_SCOPE,
+  INVALID_REQUEST_PATH,
+};
+
 const char *accessTokenStatusToString(const AccessTokenStatus &state);
 
+/**
+ * Validates whether a request path falls within the scope claim of an access 
token.
+ * Matching is performed on normalized path segments. An empty or absent scope 
is
+ * treated as unrestricted (returns true).
+ *
+ * @param[in] requestPath The incoming HTTP requestPath.
+ * @param[in] scope The scope string extracted from the token.
+ * @return True if the path is permitted by the scope, false otherwise.
+ */
+ScopeValidationResult validateScopeDetailed(StringView requestPath, StringView 
Scope);

Review Comment:
   The declaration returns `ScopeValidationResult`, but this documentation says 
the function returns a boolean. That is especially misleading for callers 
because the implementation distinguishes invalid scopes and invalid request 
paths from an ordinary out-of-scope result; document the enum result here (and 
reserve the boolean wording for `validateScope`).



-- 
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