From bfcc89877b6db6efc790a5b8c7db02c468f0d64b Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 3 Nov 2026 02:14:11 +0000 Subject: [PATCH] fix(diff-scope): a token that starts or ends with auth is an auth path; author* is not (review fix, GST-1065) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- bin/gstack-diff-scope | 15 ++++++++++----- test/diff-scope.test.ts | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/bin/gstack-diff-scope b/bin/gstack-diff-scope index 02a2810b5..ea7281284 100755 --- a/bin/gstack-diff-scope +++ b/bin/gstack-diff-scope @@ -128,16 +128,21 @@ _session_data() { return "$r" } -# An auth path names auth as a word or path segment (GST-1065), split the way -# lib/review-risk.ts tokens() splits it: camelCase, then every non-alphanumeric -# run. auth, authn, authz, oauth, oauth2 and authenticat*/authoriz* match; -# author-spec.md, AUTHORS and authority.ts do not. +# An auth path holds a token, split the way lib/review-risk.ts tokens() splits +# paths (camelCase, then every non-alphanumeric run), that starts or ends with +# auth (GST-1065): auth.ts, /auth/, authn, authz, auth0, authguard, oauth, +# basicauth, nextauth, oauth2 and authorize match; a token starting with author +# (author-spec.md, AUTHORS, authority.ts) does not unless it is authoriz*. _auth_path() { local s="$1" t r=1 while [[ "$s" =~ ^(.*[a-z0-9])([A-Z].*)$ ]]; do s="${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"; done shopt -s nocasematch for t in ${s//[^a-zA-Z0-9]/ }; do - case "$t" in auth|authn|authz|oauth|oauth2|authenticat*|authoriz*) r=0; break ;; esac + case "$t" in + authoriz*|oauth2) r=0; break ;; + author*) ;; + auth*|*auth) r=0; break ;; + esac done shopt -u nocasematch return "$r" diff --git a/test/diff-scope.test.ts b/test/diff-scope.test.ts index 6b0df6f25..e74292e35 100644 --- a/test/diff-scope.test.ts +++ b/test/diff-scope.test.ts @@ -233,9 +233,11 @@ describe('gstack-diff-scope', () => { } // Value: protects=real auth paths still raise SCOPE_AUTH after the word-boundary fix (negative controls for GST-1065); - // fails_when=the boundary drops a segment, a separator, a camelCase name or an authn/authz/oauth spelling; why_new=GST-1065; seam=bin/gstack-diff-scope + // fails_when=the boundary drops a segment, a separator, a camelCase name, an authn/authz/oauth spelling or a glued token (auth0, basicauth, nextauth); + // why_new=GST-1065 and its review (the first word-only matcher dropped glued tokens the old glob caught); seam=bin/gstack-diff-scope for (const file of ['src/auth.ts', 'app/auth/login.rb', 'lib/oauth.ts', 'src/oauth2/token.ts', 'lib/authn/check.go', 'src/authz.py', 'src/authService.ts', - 'src/useAuth.ts', 'lib/OAuthClient.ts', 'lib/authentication.rb', 'lib/authorization_policy.rb', 'src/Authorize.tsx', 'config/AUTH_KEYS.md', 'pkg/auth-middleware.go']) { + 'src/useAuth.ts', 'lib/OAuthClient.ts', 'lib/authentication.rb', 'lib/authorization_policy.rb', 'src/Authorize.tsx', 'config/AUTH_KEYS.md', 'pkg/auth-middleware.go', + 'lib/auth0.ts', 'src/basicauth.go', 'src/authguard.ts', 'lib/reauth.ts', 'pages/api/nextauth.ts', 'src/authtoken.ts', 'lib/preauth.rb', 'src/HTTPAuth.ts']) { test(`${file} is an auth path`, () => { expect(runScope(createRepo([file])).SCOPE_AUTH).toBe('true'); });