Name dinners from links with letter-only words (PR #97 review F2)
CI / ci (push) Successful in 2m25s
CI / ci (pull_request) Successful in 2m24s

Keep only words of at most 20 letters from the link's last path segment,
so punctuated or long token text such as /stew-t=abcdef never reaches
the dinner name, and stop at combined credential words like accesstoken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 20:48:03 -04:00
co-authored by Claude Opus 5.5
parent 903a332375
commit 4e0c31b7d6
2 changed files with 22 additions and 7 deletions
+18 -7
View File
@@ -500,15 +500,21 @@ func checkRecipeURL(raw string) (*url.URL, error) {
// can carry a personal token in its path, as in /stew-access_token-abc123,
// and the name is shown in every dinner response.
var credentialWords = map[string]bool{
"access": true, "api": true, "apikey": true, "auth": true, "credential": true,
"credentials": true, "passcode": true, "password": true, "passwd": true,
"secret": true, "session": true, "sig": true, "signature": true, "token": true,
"access": true, "accesstoken": true, "api": true, "apikey": true, "auth": true,
"authtoken": true, "bearer": true, "credential": true, "credentials": true,
"jwt": true, "passcode": true, "password": true, "passwd": true, "secret": true,
"session": true, "sig": true, "signature": true, "token": true,
}
// maxSlugWordChars bounds a word nameFromURL keeps: a longer run of letters
// is more likely a token than a dish.
const maxSlugWordChars = 20
// nameFromURL names a dinner from the last path segment of u: separators
// become spaces, a file extension and words holding a digit are dropped, the
// name ends at the first of credentialWords and the first letter is
// capitalized, so https://example.com/recipes/12345/chicken-tikka-masala/ is
// become spaces, a file extension is dropped, only words of at most
// maxSlugWordChars letters are kept, the name ends at the first of
// credentialWords and the first letter is capitalized, so
// https://example.com/recipes/12345/chicken-tikka-masala/ is
// "Chicken tikka masala". ok is false when no usable name is left.
func nameFromURL(u *url.URL) (name string, ok bool) {
segments := strings.FieldsFunc(u.Path, func(r rune) bool { return r == '/' })
@@ -525,7 +531,7 @@ func nameFromURL(u *url.URL) (name string, ok bool) {
if credentialWords[w] {
break
}
if strings.IndexFunc(w, unicode.IsDigit) < 0 {
if utf8.RuneCountInString(w) <= maxSlugWordChars && strings.IndexFunc(w, notLetter) < 0 {
words = append(words, w)
}
}
@@ -539,6 +545,11 @@ func nameFromURL(u *url.URL) (name string, ok bool) {
return name, true
}
// notLetter reports whether r is neither a letter nor a combining mark.
func notLetter(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsMark(r)
}
// checkUnique returns a *DuplicateError when name is harness.Similar to an
// unarchived dinner other than self, or recipeURL is one's recipe link: the
// source_url of the recipe it is grounded in, else the recipe_url still
+4
View File
@@ -26,6 +26,10 @@ func TestNameFromURL(t *testing.T) {
{"https://example.com/stew-x7k2p9q4", "Stew"},
{"https://example.com/recipes/token-abcdefghijk", ""},
{"https://example.com/key-lime-pie", "Key lime pie"},
{"https://example.com/stew-t=abcdef", "Stew"},
{"https://example.com/stew-accesstoken-abcdefghijk", "Stew"},
{"https://example.com/stew-abcdefghijklmnopqrstuvwxyz", "Stew"},
{"https://example.com/cre%CC%80me-bru%CC%82le%CC%81e", "Cre\u0300me bru\u0302le\u0301e"},
}
for _, tt := range tests {
u, err := url.Parse(tt.link)