fix: a loop through the site's absolute URL lists each address once

The loop and chain path of a redirect notice appended the final target
when it was an absolute URL of the site itself, so a loop read
"/a → /b → /a → https://example.test/a". The site URL now counts as
internal, as it does when the chain is followed.
This commit is contained in:
2026-10-27 13:15:36 +01:00
parent e957c00866
commit 79567b3e18
2 changed files with 9 additions and 7 deletions

View File

@@ -170,7 +170,7 @@ func (s *Service) Issues(ctx context.Context) ([]Issue, error) {
continue
}
out = append(out, Issue{Kind: IssueRedirectChain, URL: r.Source, Tool: toolUpdateRedirect, Details: map[string]any{
"redirect_id": r.ID, "hops": len(ch.Hops), "final": displayURL(site, ch.Final), "path": displayPath(r, ch),
"redirect_id": r.ID, "hops": len(ch.Hops), "final": displayURL(site, ch.Final), "path": displayPath(site, r, ch),
}})
}

View File

@@ -203,11 +203,11 @@ func checkRedirectRules(site string, rules []models.Redirect, r models.Redirect)
return check, err
}
if ch.Loop {
check.Loop, check.Path = true, displayPath(r, ch)
check.Loop, check.Path = true, displayPath(site, r, ch)
return check, nil
}
if len(ch.Hops) > 1 && ch.Hops[0].Rule.ID == r.ID {
check.Chain, check.Path, check.Hops = true, displayPath(r, ch), len(ch.Hops)
check.Chain, check.Path, check.Hops = true, displayPath(site, r, ch), len(ch.Hops)
check.Source, check.Final = r.Source, displayURL(site, ch.Final)
return check, nil
}
@@ -233,15 +233,17 @@ func checkRedirectRules(site string, rules []models.Redirect, r models.Redirect)
return check, err
}
check.Chain, check.Hops = true, len(ch.Hops)+1
check.Path = append([]string{other.Source}, displayPath(r, ch)...)
check.Path = append([]string{other.Source}, displayPath(site, r, ch)...)
check.Source, check.Final = other.Source, displayURL(site, ch.Final)
return check, nil
}
return check, nil
}
// displayPath lists a chain's URLs for a notice, the source of r first.
func displayPath(r models.Redirect, ch Chain) []string {
// displayPath lists a chain's URLs for a notice, the source of r first. A
// final target outside the site ends the list; one on the site (a path or
// the site's absolute URL) is already its last visited path.
func displayPath(site string, r models.Redirect, ch Chain) []string {
out := make([]string, 0, len(ch.Visited))
for i, v := range ch.Visited {
if i == 0 && r.Match != models.RedirectExact && len(ch.Hops) > 0 && ch.Hops[0].Rule.ID == r.ID {
@@ -251,7 +253,7 @@ func displayPath(r models.Redirect, ch Chain) []string {
}
if len(ch.Hops) > 0 && ch.Final != "" {
if last := out[len(out)-1]; last != ch.Final {
if _, _, internal := internalTarget("", ch.Final); !internal {
if _, _, internal := internalTarget(site, ch.Final); !internal {
out = append(out, ch.Final)
}
}