⋯ 171 hidden lines@@ -172,6 +172,24 @@ func (c *CI) answer(ctx context.Context, rs *runState, j *domain.CIJob, p action 172 ra := ports.ResolvedAction{ 172 ra := ports.ResolvedAction{ 173 Ref: p.uses, NameWithOwner: res.NameWithOwner, Path: p.path, ResolvedSHA: res.ResolvedSHA, Source: res.Source, 173 Ref: p.uses, NameWithOwner: res.NameWithOwner, Path: p.path, ResolvedSHA: res.ResolvedSHA, Source: res.Source, 174 } 174 } 175 + notFound := ports.ResolvedAction{Ref: p.uses, Error: errUnresolvedRepo(p)} 176 + 177 + // A repository of this server that the pusher cannot read is never served 178 + // from anywhere else: not from github.com under the same name, whatever 179 + // source an earlier resolution of the attempt stored. It answers as a 180 + // repository that does not exist. (When it does not exist on this server, a 181 + // stored ermite resolution has nothing to serve either.) 182 + repo, onErmite, err := c.ermiteRepoOf(ctx, p) 183 + if err != nil { 184 + return ports.ResolvedAction{}, err 185 + } 186 + if onErmite { 187 + if ok, err := c.pusherCanRead(ctx, by, rs.run.Repo, repo); err != nil { 188 + return ports.ResolvedAction{}, err 189 + } else if !ok { 190 + return notFound, nil 191 + } 192 + } 175 switch res.Source { 193 switch res.Source { 176 case domain.CIActionGitHub: 194 case domain.CIActionGitHub: 177 // The case the workflow wrote: the stored resolution is shared by 195 // The case the workflow wrote: the stored resolution is shared by ⋯ 1 hidden line@@ -179,23 +197,11 @@ func (c *CI) answer(ctx context.Context, rs *runState, j *domain.CIJob, p action 179 ra.NameWithOwner = p.owner + "/" + p.repo 197 ra.NameWithOwner = p.owner + "/" + p.repo 180 ra.TarballURL = "https://codeload.github.com/" + ra.NameWithOwner + "/tar.gz/" + res.ResolvedSHA 198 ra.TarballURL = "https://codeload.github.com/" + ra.NameWithOwner + "/tar.gz/" + res.ResolvedSHA 181 case domain.CIActionErmite: 199 case domain.CIActionErmite: 182 - name, err := domain.ParseRepoName(strings.ToLower(p.repo)) 200 + if !onErmite { 183 - if err != nil { 201 + return notFound, nil 184 - return ports.ResolvedAction{Ref: p.uses, Error: errUnresolvedRepo(p)}, nil 185 - } 186 - repo, err := c.cfg.Meta.Get(ctx, name) 187 - if errors.Is(err, domain.ErrNotFound) { 188 - return ports.ResolvedAction{Ref: p.uses, Error: errUnresolvedRepo(p)}, nil 189 - } else if err != nil { 190 - return ports.ResolvedAction{}, err 191 - } 192 - if ok, err := c.pusherCanRead(ctx, by, rs.run.Repo, repo); err != nil { 193 - return ports.ResolvedAction{}, err 194 - } else if !ok { 195 - return ports.ResolvedAction{Ref: p.uses, Error: errUnresolvedRepo(p)}, nil 196 } 202 } 197 ra.TarballURL = c.cfg.PublicURL + apiPath + "/repos/" + res.NameWithOwner + "/tarball/" + res.ResolvedSHA 203 ra.TarballURL = c.cfg.PublicURL + apiPath + "/repos/" + res.NameWithOwner + "/tarball/" + res.ResolvedSHA 198 - ra.Token = c.archiveToken(rs, j, name, res.ResolvedSHA) 204 + ra.Token = c.archiveToken(rs, j, repo.Name, res.ResolvedSHA) 199 } 205 } 200 return ra, nil 206 return ra, nil 201 } 207 } ⋯ 19 hidden lines@@ -221,6 +227,29 @@ func (c *CI) archiveToken(rs *runState, j *domain.CIJob, repo domain.RepoName, s 221 return tok 227 return tok 222 } 228 } 223 229 230 + // ermiteRepoOf finds the repository of this server that a reference names: the 231 + // owner is ERMITE_OWNER (without regard to case) and the repository exists. ok 232 + // is false for any other reference, which github.com serves. It is the one 233 + // place that decides the source of an action, for a first resolution and for the 234 + // answer from a stored one. 235 + func (c *CI) ermiteRepoOf(ctx context.Context, a actionRef) (repo domain.Repo, ok bool, err error) {236 + if !domain.OwnerMatches(c.cfg.Owner, a.owner) { 237 + return domain.Repo{}, false , nil 238 + } 239 + name, perr := domain.ParseRepoName(strings.ToLower(a.repo)) 240 + if perr != nil { 241 + return domain.Repo{}, false , nil // not a name ermite can hold 242 + } 243 + repo, err = c.cfg.Meta.Get(ctx, name) 244 + switch { 245 + case errors.Is(err, domain.ErrNotFound): 246 + return domain.Repo{}, false , nil 247 + case err != nil : 248 + return domain.Repo{}, false , err 249 + } 250 + return repo, true , nil 251 + } 252 + 224 // pusherAccess is who the actions of a run are resolved on behalf of: the 253 // pusherAccess is who the actions of a run are resolved on behalf of: the 225 // pusher recorded with the run. 254 // pusher recorded with the run. 226 type pusherAccess struct { pusher domain.CIPusher }255 type pusherAccess struct { pusher domain.CIPusher } ⋯ 116 hidden lines@@ -343,30 +372,28 @@ func errUnresolvedRepo(a actionRef) string { 343 // does not resolve; err is a failure of the call. 372 // does not resolve; err is a failure of the call. 344 func (c *CI) resolveAction(ctx context.Context, j domain.CIJob, a actionRef, remote *remoteRefs, by pusherAccess) (res domain.CIActionResolution, msg string, err error) {373 func (c *CI) resolveAction(ctx context.Context, j domain.CIJob, a actionRef, remote *remoteRefs, by pusherAccess) (res domain.CIActionResolution, msg string, err error) {345 res = domain.CIActionResolution{RunID: j.RunID, RunAttempt: j.RunAttempt, Key: a.key()} 374 res = domain.CIActionResolution{RunID: j.RunID, RunAttempt: j.RunAttempt, Key: a.key()} 346 - if domain.OwnerMatches(c.cfg.Owner, a.owner) { 375 + repo, onErmite, err := c.ermiteRepoOf(ctx, a) 347 - if name, perr := domain.ParseRepoName(strings.ToLower(a.repo)); perr == nil { 376 + if err != nil { 348 - if repo, gerr := c.cfg.Meta.Get(ctx, name); gerr == nil { 377 + return res, "" , err 349 - // A repository the pusher may not read answers as one that does 378 + } 350 - // not exist, before any ref is looked at, so that the texts leak 379 + if onErmite { 351 - // nothing about it. 380 + // The repository exists here, so github.com never answers for it. A 352 - if ok, err := c.pusherCanRead(ctx, by, j.Repo, repo); err != nil { 381 + // repository the pusher may not read answers as one that does not exist, 353 - return res, "" , err 382 + // before any ref is looked at, so that the texts leak nothing about it. 354 - } else if !ok { 383 + if ok, err := c.pusherCanRead(ctx, by, j.Repo, repo); err != nil { 355 - return res, errUnresolvedRepo(a), nil 384 + return res, "" , err 356 - } 385 + } else if !ok { 357 - sha, found, err := c.resolveOnErmite(ctx, name, a.ref) 386 + return res, errUnresolvedRepo(a), nil 358 - if err != nil { 359 - return res, "" , err 360 - } 361 - if !found { 362 - return res, errUnresolvedRef(a), nil 363 - } 364 - res.NameWithOwner, res.ResolvedSHA, res.Source = c.cfg.Owner+"/" +name.String(), sha, domain.CIActionErmite 365 - return res, "" , nil 366 - } else if !errors.Is(gerr, domain.ErrNotFound) { 367 - return res, "" , gerr 368 - } 369 } 387 } 388 + sha, found, err := c.resolveOnErmite(ctx, repo.Name, a.ref) 389 + if err != nil { 390 + return res, "" , err 391 + } 392 + if !found { 393 + return res, errUnresolvedRef(a), nil 394 + } 395 + res.NameWithOwner, res.ResolvedSHA, res.Source = c.cfg.Owner+"/" +repo.Name.String(), sha, domain.CIActionErmite 396 + return res, "" , nil 370 } 397 } 371 res.NameWithOwner, res.Source = a.owner+"/" +a.repo, domain.CIActionGitHub 398 res.NameWithOwner, res.Source = a.owner+"/" +a.repo, domain.CIActionGitHub 372 if low := strings.ToLower(a.ref); isHex40(low) { 399 if low := strings.ToLower(a.ref); isHex40(low) {