diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index ea51eb6791..d5e78ad4a5 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -104,6 +104,12 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) { return } + taskRepo, err := repo_perm_model.GetRepositoryByID(ctx, task.RepoID) + if err != nil { + ctx.HTTPError(http.StatusInternalServerError, "GetRepositoryByID", err.Error()) + return + } + var packageRepoID int64 if ctx.Package.Descriptor != nil && ctx.Package.Descriptor.Package != nil { packageRepoID = ctx.Package.Descriptor.Package.RepoID @@ -120,48 +126,54 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) { } } - log.Error("DEBUG: packageRepoID=%d, task.RepoID=%d", packageRepoID, task.RepoID) + var grantedMode perm.AccessMode if packageRepoID == 0 { - ctx.HTTPError(http.StatusForbidden, "reqPackageAccess", "Actions tokens cannot access packages not linked to a repository") - return - } - - if task.RepoID != packageRepoID { - // Cross-repository access - only allowed for Orgs with explicit policy - if !ctx.Package.Owner.IsOrganization() { - ctx.HTTPError(http.StatusForbidden, "reqPackageAccess", "cross-repository package access is only allowed for organizations") + // Package is unlinked (or new/doesn't exist yet). + // Only allow if the Action's Repository Owner matches the Package Owner. + if taskRepo.OwnerID != ctx.Package.Owner.ID { + ctx.HTTPError(http.StatusForbidden, "reqPackageAccess", "Actions tokens cannot access unlinked packages from a different owner") return } - - cfg, err := actions_model.GetOrgActionsConfig(ctx, ctx.Package.Owner.ID) - if err != nil { - log.Error("GetOrgActionsConfig: %v", err) - ctx.HTTPError(http.StatusInternalServerError, "GetOrgActionsConfig", err.Error()) - return - } - // Use selective cross-repo access check - if !cfg.IsRepoAllowedCrossAccess(packageRepoID) { - ctx.HTTPError(http.StatusForbidden, "reqPackageAccess", "cross-repository package access is not allowed for this repository") - return - } - } - - var grantedMode perm.AccessMode - if task.RepoID == packageRepoID { - // Same-repository access: use the token's configured package permission + // Treat as "Same Owner/Repo" scope - allow configured permissions if err := task.LoadJob(ctx); err == nil && task.Job != nil { perms, _ := repo_perm_model.UnmarshalTokenPermissions(task.Job.TokenPermissions) grantedMode = perms.Packages } else { - // Fallback if job cannot be loaded grantedMode = perm.AccessModeRead } } else { - // Cross-repository access: strictly Read-only even if token/policy allow more - grantedMode = perm.AccessModeRead + // Package is linked to a repository + if task.RepoID == packageRepoID { + // Same-repository access: use the token's configured package permission + if err := task.LoadJob(ctx); err == nil && task.Job != nil { + perms, _ := repo_perm_model.UnmarshalTokenPermissions(task.Job.TokenPermissions) + grantedMode = perms.Packages + } else { + grantedMode = perm.AccessModeRead + } + } else { + // Cross-repository access - only allowed for Orgs with explicit policy + if !ctx.Package.Owner.IsOrganization() { + ctx.HTTPError(http.StatusForbidden, "reqPackageAccess", "cross-repository package access is only allowed for organizations") + return + } + + cfg, err := actions_model.GetOrgActionsConfig(ctx, ctx.Package.Owner.ID) + if err != nil { + log.Error("GetOrgActionsConfig: %v", err) + ctx.HTTPError(http.StatusInternalServerError, "GetOrgActionsConfig", err.Error()) + return + } + // Use selective cross-repo access check + if !cfg.IsRepoAllowedCrossAccess(packageRepoID) { + ctx.HTTPError(http.StatusForbidden, "reqPackageAccess", "cross-repository package access is not allowed for this repository") + return + } + // Cross-repository access: strictly Read-only even if token/policy allow more + grantedMode = perm.AccessModeRead + } } - log.Error("DEBUG: grantedMode=%d, accessMode=%d", grantedMode, accessMode) // If all security checks pass, ensure the context has at least the granted permission. // This effectively "boosts" the Actions token's permissions for the targeted package.