Skip to content

fix(#59): clamp meta.last page to minimum of 1 for empty results#1340

Open
fullsend-ai-coder[bot] wants to merge 1 commit into
masterfrom
agent/59-fix-empty-pagination-last
Open

fix(#59): clamp meta.last page to minimum of 1 for empty results#1340
fullsend-ai-coder[bot] wants to merge 1 commit into
masterfrom
agent/59-fix-empty-pagination-last

Conversation

@fullsend-ai-coder
Copy link
Copy Markdown

When a paginated endpoint returns zero items, pagination_query.pages is 0, causing meta.last to point to page=0 which is an invalid URL that returns a 404. Fix by using max(1, pagination_query.pages) so meta.last always points to at least page=1, matching meta.first for empty collections.

Add test_get_builds_empty to verify meta.last equals meta.first and contains page=1 when no builds exist.

Note: kerberos-dependent test suites could not run in the sandbox (missing libkrb5-dev). The web/API tests covering the changed code passed. Full suite verification is required in CI.


Closes #59

Post-script verification

  • Branch is not main/master (agent/59-fix-empty-pagination-last)
  • Secret scan passed (gitleaks — 6e0ac6cccda83166c240c4b0a4ebd4e8532c6f26..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

When a paginated endpoint returns zero items, pagination_query.pages
is 0, causing meta.last to point to page=0 which is an invalid URL
that returns a 404. Fix by using max(1, pagination_query.pages) so
meta.last always points to at least page=1, matching meta.first
for empty collections.

Add test_get_builds_empty to verify meta.last equals meta.first
and contains page=1 when no builds exist.

Note: kerberos-dependent test suites could not run in the sandbox
(missing libkrb5-dev). The web/API tests covering the changed code
passed. Full suite verification is required in CI.

Closes #59
@fullsend-ai-review
Copy link
Copy Markdown

Review

Findings

Critical

  • [supply-chain risk] .github/workflows/fullsend.yaml:43 — The dispatch job calls fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v0, pinned to a mutable Git tag. The fullsend-ai organization can silently change the code that runs with this repository's GITHUB_TOKEN. Combined with broad top-level permissions (actions:write, contents:write, id-token:write, issues:write, pull-requests:write) and the pull_request_target trigger (which grants write access and exposes secrets), a compromised or malicious update to the reusable workflow can write arbitrary commits, mint OIDC tokens, exfiltrate GCP secrets, and modify issues/PRs.
    Remediation: Pin the reusable workflow to a full commit SHA. Apply least-privilege by scoping permissions per-job rather than at workflow level.

High

Medium

  • [secrets-exposure] .github/workflows/fullsend.yaml:49 — Two GCP secrets (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID) are passed to the external reusable workflow pinned to a mutable tag. The pull_request_target trigger means these secrets are available even for PRs from forks. See also: [supply-chain risk] finding at this location.
    Remediation: Pin the reusable workflow to a commit SHA. Consider gating secret access behind a GitHub environment with required reviewers.

Low

  • [api-contract] iib/workers/tasks/build.py:1169verify_operators_exists returns a set, but opm_registry_rm_fbc type-hints its operators parameter as List[str]. Functionally correct (the downstream ','.join() works on any iterable), but violates the type contract.
    Remediation: Wrap with list(operators_in_db) or update the type hint to accept Iterable[str].

  • [authorization-bypass] .github/workflows/fullsend.yaml:63 — The stop-fix job allows CONTRIBUTOR association (any user who has had a PR merged) to disable the fix agent on any PR via /fs-fix-stop. This is a wider authorization surface than likely intended.
    Remediation: Remove CONTRIBUTOR from the allowed associations, keeping only OWNER, MEMBER, COLLABORATOR, plus the PR author check.

  • [missing-doc] iib/web/iib_static_types.py:11 — The PaginationMetadata TypedDict lacks documentation of the edge-case behavior where last page is clamped to minimum 1 for empty results.
    Remediation: Add a comment in pagination_metadata() explaining the clamping behavior.

Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the review comment for full details.

if: >-
github.event_name != 'issue_comment'
|| github.event.comment.user.type != 'Bot'
uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[critical] supply-chain risk

The dispatch job calls fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v0, pinned to a mutable Git tag. Combined with broad top-level permissions (actions:write, contents:write, id-token:write) and pull_request_target trigger, a compromised update to the reusable workflow can write arbitrary commits, mint OIDC tokens, and exfiltrate GCP secrets.

Suggested fix: Pin the reusable workflow to a full commit SHA. Scope permissions per-job rather than at workflow level.

# branch in reusable-dispatch.yml — zero changes to this repo.
name: fullsend

permissions:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[high] excessive-permissions

Workflow-level permissions block grants contents:write, actions:write, and id-token:write to every job that does not override them. The dispatch job inherits all of these and hands them to an external, tag-pinned reusable workflow.

Suggested fix: Move permissions into individual jobs. Declare only the permissions that reusable-dispatch.yml actually requires.

install_mode: per-repo
mint_url: ${{ vars.FULLSEND_MINT_URL }}
gcp_region: ${{ vars.FULLSEND_GCP_REGION }}
secrets:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] secrets-exposure

Two GCP secrets (FULLSEND_GCP_WIF_PROVIDER, FULLSEND_GCP_PROJECT_ID) are passed to the external reusable workflow pinned to a mutable tag. The pull_request_target trigger means these secrets are available even for PRs from forks.

Suggested fix: Pin the reusable workflow to a commit SHA. Consider gating secret access behind a GitHub environment with required reviewers.

base_dir=temp_dir,
from_index=from_index_resolved,
operators=operators,
operators=operators_in_db,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] api-contract

verify_operators_exists returns a set but opm_registry_rm_fbc type-hints its operators parameter as List[str]. Functionally correct but violates the type contract.

Suggested fix: Wrap with list(operators_in_db) or update the type hint to accept Iterable[str].

github.event.comment.author_association == 'OWNER'
|| github.event.comment.author_association == 'MEMBER'
|| github.event.comment.author_association == 'COLLABORATOR'
|| github.event.comment.author_association == 'CONTRIBUTOR'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] authorization-bypass

The stop-fix job allows CONTRIBUTOR association (any user who has had a PR merged) to disable the fix agent on any PR via /fs-fix-stop. This is a wider authorization surface than likely intended.

Suggested fix: Remove CONTRIBUTOR from the allowed associations, keeping only OWNER, MEMBER, COLLABORATOR, plus the PR author check.

@yashvardhannanavati yashvardhannanavati changed the base branch from main to master June 5, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants