Skip to content

Add public found item contact page#1487

Open
mizoz wants to merge 2 commits into
sysadminsmedia:mainfrom
mizoz:fix/found-item-contact-page-13
Open

Add public found item contact page#1487
mizoz wants to merge 2 commits into
sysadminsmedia:mainfrom
mizoz:fix/found-item-contact-page-13

Conversation

@mizoz
Copy link
Copy Markdown

@mizoz mizoz commented May 10, 2026

Summary

  • add rate-limited public found-label endpoints for item UUID labels and asset ID labels
  • redirect signed-out /item/:id, /a/:id, and /assets/:id visits to a public found-item page
  • show a minimal owner contact page with a mailto link and sign-in option
  • regenerate Swagger/OpenAPI docs and frontend API contracts

Fixes #13.

Privacy notes

The public response intentionally returns only itemId and ownerEmail. It does not expose item names, notes, photos, attachments, locations, or other inventory details. Asset ID lookups return contact only when the asset ID resolves to exactly one entity.

Testing

  • go test ./...
  • go test ./app/api ./app/api/handlers/v1 ./internal/data/repo
  • pnpm exec eslint middleware/auth.ts lib/api/public.ts 'pages/found/[kind]/[id].vue' --max-warnings 0
  • pnpm exec prettier --check --arrow-parens avoid --tab-width 2 --vue-indent-script-and-style --single-quote false --trailing-comma es5 --print-width 120 middleware/auth.ts lib/api/public.ts 'pages/found/[kind]/[id].vue'
  • Browser check: signed-out /item/:id redirects to /found/item/:id and signed-out /a/:id redirects to /found/asset/:id; both render the contact mailto link against a local backend.

Known existing checks

  • pnpm run lint:ci still fails on existing unrelated lint errors outside this patch, for example unused variables in location/item pages and no-explicit-any in lib/api/classes/items.ts.
  • pnpm run typecheck still fails on existing generated/ref type issues across the frontend, including EntitySummary.location, several useAsyncData ref usages, and template field shape errors.

Summary by CodeRabbit

  • New Features

    • New GET endpoints to fetch owner contact for found items/assets.
    • New public "Did you find this item?" page with email owner and Sign in flow returning to the label.
    • Frontend API methods to fetch found-item contact info.
  • Bug Fixes / UX

    • Route middleware updated to redirect label routes for unauthenticated visitors.
  • Rate Limiting

    • Applied rate limit to found-item endpoints (60 req/min).
  • Documentation

    • OpenAPI/Swagger updated with endpoints, response schema, and group↔user join model.
  • Tests

    • Added repository tests for found-item contact lookups.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b66f5aa8-a1ca-42a4-8792-d4cb917e65a4

📥 Commits

Reviewing files that changed from the base of the PR and between 942348b and 5253c09.

📒 Files selected for processing (4)
  • frontend/lib/api/public.ts
  • frontend/locales/en.json
  • frontend/middleware/auth.ts
  • frontend/pages/found/[kind]/[id].vue
✅ Files skipped from review due to trivial changes (1)
  • frontend/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/lib/api/public.ts
  • frontend/middleware/auth.ts
  • frontend/pages/found/[kind]/[id].vue

Walkthrough

Adds an unauthenticated "found" flow: backend repo methods and rate-limited API endpoints return owner contact for an entity or asset ID; frontend middleware redirects scanned label routes to a new /found page that fetches the contact and offers a mailto link; OpenAPI/Swagger and tests updated.

Changes

Found Item Contact Flow

Layer / File(s) Summary
Data Contract & Schema
backend/internal/data/repo/repo_entities.go, frontend/lib/api/types/data-contracts.ts
FoundEntityContact DTO/interface added (itemId/ItemID, ownerEmail/OwnerEmail).
Repository Layer: Contact Data Access
backend/internal/data/repo/repo_entities.go
Added GetFoundEntityContact, GetFoundEntityContactByAssetID, helper foundEntityContactForGroup, and ent/usergroup import for owner membership lookup.
Repository Tests
backend/internal/data/repo/repo_entities_found_item_test.go
Tests for entity-ID lookup, asset-ID lookup, ambiguous-asset error, and not-found behavior.
Backend API: Handlers, Routes & Rate Limiting
backend/app/api/app.go, backend/app/api/handlers/v1/v1_ctrl_found_item.go, backend/app/api/routes.go
New foundLabelLimiter (60 req/min) init; HandleFoundEntityContact and HandleFoundAssetContact handlers; routes GET /api/v1/found/entities/{id} and GET /api/v1/found/assets/{id} with limiter middleware.
API Documentation
docs/public/api/*
Added /v1/found endpoints returning repo.FoundEntityContact; introduced ent.UserGroup/ent.UserGroupEdges; renamed user.Roleusergroup.Role; removed POST /v1/groups/members and v1.GroupMemberAdd.
Frontend API Client
frontend/lib/api/public.ts
Added foundEntityContact(id) and foundAssetContact(id) methods (typed GETs); minor import/register formatting changes.
Frontend Auth Middleware & Routing
frontend/middleware/auth.ts
Middleware accepts to route, foundLabelPath helper added, unauthenticated label routes redirected to /found/* and authRedirect set.
Frontend Found Item Page
frontend/pages/found/[kind]/[id].vue
New page uses useAsyncData to fetch contact, builds mailto: link, shows email button when available, and offers sign-in redirect preserving authRedirect.
Frontend Translations
frontend/locales/en.json
Added pages.found.* translation keys for the found-item UI and email templates.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Limiter as foundLabelLimiter
  participant Handler as V1Controller
  participant Repo as EntityRepository
  participant DB as Database

  Client->>Limiter: GET /api/v1/found/entities/{id}
  Limiter->>Handler: allow -> HandleFoundEntityContact
  Handler->>Repo: GetFoundEntityContact(id)
  Repo->>DB: fetch entity/group, query UserGroup(RoleOwner)
  DB-->>Repo: owner email, item id
  Repo-->>Handler: FoundEntityContact
  Handler-->>Client: 200 + JSON
Loading

Security Recommendations

  • Limit exposure of raw owner emails to unauthenticated callers; consider redaction or rate/verification thresholds beyond a global limiter.
  • Ensure label IDs are sufficiently unguessable and monitor for enumeration attempts.
  • Log and alert on repeated not-found or ambiguous-asset patterns to detect scraping.
  • Review the role/schema changes to confirm no unintended public surfaces expose membership details.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • tankerkiller125
  • katosdev
  • tonyaellie

Poem

🔍 A QR code lost, a finder scans the light,
📧 A single tap sends a note polite,
Rate-limited routes and a welcome page,
Lost meets found — across the web stage.
✨ Homebound items, one gentle flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main feature: adding a public found item contact page for signed-out users.
Description check ✅ Passed The description is comprehensive and covers type of PR (feature), what it does, which issue it fixes (#13), privacy considerations, and detailed testing performed.
Linked Issues check ✅ Passed The PR fully addresses issue #13 by implementing a public found item contact page with mailto links when users scan QR codes while unsigned in, exposing only minimal contact data.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the found item contact feature: rate limiters, API endpoints, frontend page/middleware, localization, and documentation updates are all necessary for the stated objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added ⬆️ enhancement New feature or request review needed A review is needed on this PR or Issue go Pull requests that update Go code labels May 10, 2026
@mizoz mizoz force-pushed the fix/found-item-contact-page-13 branch from 941dc35 to 942348b Compare May 10, 2026 23:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 4

🧹 Nitpick comments (6)
backend/app/api/app.go (1)

42-42: Security hardening: make this public limiter tunable and observable.

Because these endpoints are unauthenticated and return contact emails, consider moving 60/min to config and adding metrics/alerts on 429 spikes to react quickly to scraping attempts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/api/app.go` at line 42, Move the hardcoded 60/min into
configuration and add observability: add a new config field (e.g.,
Options.FoundLabelRatePerMinute or FoundLabelRateLimit) and use it when
constructing s.foundLabelLimiter via newSimpleRateLimiter instead of the literal
60; then instrument the rate limiter rejection path (where handlers return 429)
to increment a metric/counter (e.g., found_label_rate_limited_total) and expose
it to your metrics system so alerts can be created on spikes; reference
s.foundLabelLimiter, newSimpleRateLimiter, and s.conf.Options.TrustProxy when
making the change and update any config parsing/validation to ensure the value
is tunable at runtime if supported.
frontend/pages/found/[kind]/[id].vue (1)

11-13: ⚡ Quick win

Move user-facing strings to translation keys.

This page introduces several hardcoded UI strings (title, headings, button labels, loading/error copy) that should be i18n-backed for consistency with localized UI.

As per coding guidelines **/*.{ts,vue}: “Check for hardcoded strings in UI components that should be translatable.”

Also applies to: 63-67, 73-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/found/`[kind]/[id].vue around lines 11 - 13, The page
currently uses hardcoded UI strings (e.g. the head title set via
useHead("HomeBox | Found Item"), and the headings, button labels, and
loading/error copy referenced around the noted ranges) — replace all of those
with i18n translation keys by wiring up the vue-i18n APIs (e.g. import/use
useI18n or $t) in the component, change literal strings in the script/template
to calls like t('found.item.title') or $t('found.item.loading'), and add
corresponding keys to the locale files; ensure the useHead title uses the
translated value (t('found.item.title')) and update all button labels, headings,
and loading/error messages to use the new keys (look for uses in the component
methods and template such as useHead, template text nodes, and any button/alert
text).
docs/public/api/swagger-2.0.json (1)

1173-1180: ⚡ Quick win

Document abuse-control and miss-path responses for public contact endpoints.

For public email-revealing routes, add explicit 429 (rate limit) and 404/422 responses in the OpenAPI contract so clients can implement safe handling and backoff behavior.

Also applies to: 1201-1208

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/api/swagger-2.0.json` around lines 1173 - 1180, Update the
operation responses that currently return "200" with "$ref":
"#/definitions/repo.FoundEntityContact" (i.e., the public
contact/email-revealing endpoints) to include explicit "429" and appropriate
"404"/"422" response entries: add a "429" response with a short description like
"Too Many Requests" and a schema pointing to your existing error model (e.g.,
"#/definitions/Error" or the project's validation/error definition), add a "404"
response with description "Not Found" and an error schema, and add a "422"
response with description "Unprocessable Entity" (validation error) and an
error/validation schema; ensure these new response objects sit alongside the
existing "200" response for the same operation(s) that reference
repo.FoundEntityContact so clients can handle rate-limiting and
missing/validation cases.
docs/public/api/openapi-3.0.json (1)

5394-5404: 💤 Low value

repo.FoundEntityContact minimalism is 👌 — but consider marking ownerEmail shape expectations.

The DTO is appropriately spartan (just itemId and ownerEmail), which is exactly the privacy contract the PR promises. Two tiny optional polish ideas at the swagger-source level:

  • Add format: "email" (or "uuid" for itemId) so generated clients get free validation/typing.
  • Add an example block to make the public docs self-explanatory.

Not blocking; the contract is correct as-is.

Security recommendation: because this endpoint is unauthenticated and emits an email address keyed by a guessable-ish asset ID (small integer in many setups), please make sure the implementation also (a) is rate-limited per-IP and globally for the endpoint to deter scraping/enumeration of ownerEmail, (b) logs are sanitized so the returned email isn't written to access logs at info level, and (c) consider whether a future iteration should return a tokenized "contact this owner" relay rather than the raw email. Out of scope for the doc, but worth tracking. 🛡️

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/api/openapi-3.0.json` around lines 5394 - 5404, Update the
OpenAPI schema for repo.FoundEntityContact by adding shape metadata: set
"ownerEmail" to include format: "email" and set "itemId" to an appropriate
format (e.g., "uuid" or "int64" per your actual ID type), and add an "example"
object showing a realistic itemId and ownerEmail to improve generated-client
validation and docs; reference the repo.FoundEntityContact schema and its
properties ("ownerEmail", "itemId") when making these edits. Also, though not a
doc change, ensure the implementation of the unauthenticated endpoint is
tracked/fixed to enforce per-IP and global rate-limiting, sanitize logs to avoid
writing returned emails at info level, and consider returning a relay/tokenized
contact instead of raw ownerEmail in a future iteration.
docs/public/api/swagger-2.0.yaml (1)

1990-1999: 💤 Low value

Duplicate enum value may cause confusion.

The usergroup.Role enum lists "user" twice (lines 1992-1993), similar to the pattern in authroles.Role. While this appears to be a schema generation artifact where the default value is listed separately, it could be confusing for API consumers.

Consider documenting why "user" appears twice, or ensure the OpenAPI generator is configured to avoid duplicate enum values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/api/swagger-2.0.yaml` around lines 1990 - 1999, The OpenAPI enum
for usergroup.Role contains a duplicate "user" entry which can confuse clients;
update the schema generation or the YAML so enum values are unique by removing
the duplicate "user" entry (or post-process the generated enum) and ensure
x-enum-varnames still map correctly (DefaultRole, RoleUser, RoleOwner); also
check the similar authroles.Role generation to apply the same deduplication rule
so both enums only list each value once.
docs/public/api/openapi-3.0.yaml (1)

3881-3890: 💤 Low value

Duplicate enum value may cause confusion.

The usergroup.Role enum lists "user" twice (lines 3884-3885), matching the pattern in the Swagger 2.0 spec. While likely a schema generation artifact, this could confuse API consumers. Consider documenting why the default role is listed separately or adjusting the code generation to avoid duplicates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/api/openapi-3.0.yaml` around lines 3881 - 3890, The
usergroup.Role enum contains a duplicated value "user" which can confuse
consumers; update the schema so the enum array contains unique values (e.g.,
["user","owner"]) and ensure x-enum-varnames aligns with those values (e.g.,
DefaultRole/RoleUser/RoleOwner only if they map to distinct enum entries), or if
the duplicate represents a modeled default, replace the duplicate with a
documented sentinel (or remove the DefaultRole duplicate) and adjust the
generator that produces usergroup.Role so it does not emit duplicate enum
entries; locate this in the OpenAPI definition under the usergroup.Role schema
and update the enum and x-enum-varnames accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/public/api/openapi-3.0.yaml`:
- Around line 756-793: The OpenAPI paths "/v1/found/assets/{id}" and
"/v1/found/entities/{id}" currently expose PII via the repo.FoundEntityContact
schema (ownerEmail); update the spec to eliminate or mask ownerEmail from that
schema (or replace it with a non-PII field like ownerContactMasked) and add
security requirements and rate limiting notes to both path definitions (e.g., a
security requirement referencing existing securitySchemes and a description of
rate-limiting/anti-scraping measures), add a privacy/security warning and link
to the privacy policy in the path summaries or descriptions, and add a
recommended alternative endpoint or mechanism (contact form/one-time token) in
the documentation for contacting owners; ensure the change updates the
components/schemas/repo.FoundEntityContact reference so no public response
returns raw ownerEmail.

In `@docs/public/api/swagger-2.0.yaml`:
- Around line 2883-2918: The two public GET paths (/v1/found/assets/{id} and
/v1/found/entities/{id}) expose PII via the repo.FoundEntityContact schema
(ownerEmail) and lack any security/rate-limit documentation; update these
endpoint specs to require authentication or a documented security scheme (add a
security: section), remove or replace ownerEmail in the response schema
repo.FoundEntityContact with a non-PII alternative (e.g., contactProxy,
maskedEmail, or token) and document the privacy implications, add X-RateLimit
response header definitions and a brief note linking to the privacy policy and
opt-out instructions, and if you cannot change behavior, add an explicit warning
in the endpoint descriptions that ownerEmail is PII and rate-limited
access/abuse logging is enforced.

In `@frontend/lib/api/public.ts`:
- Around line 40-49: In foundEntityContact and foundAssetContact, encode the id
before interpolating into the URL so reserved characters won't break the route;
replace direct interpolation of id in route(`/found/entities/${id}`) and
route(`/found/assets/${id}`) with an encoded value (use encodeURIComponent or
equivalent) so the generated path is safe for all id values.

In `@frontend/middleware/auth.ts`:
- Around line 35-43: The current regexes used with path.match (the itemMatch and
assetMatch checks in frontend/middleware/auth.ts) should be anchored to prevent
matching nested routes; change the patterns to require the end of the path
(allowing an optional trailing slash) — e.g. replace the item pattern with one
that enforces ^/item/([^/]+)(?:/)?$ and the assets pattern with
^/(?:a|assets)/([^/]+)(?:/)?$ so only top-level /item/:id or /assets/:id (with
optional trailing slash) get redirected, keeping the same
encodeURIComponent(itemMatch[1]) and encodeURIComponent(assetMatch[1]) usage.

---

Nitpick comments:
In `@backend/app/api/app.go`:
- Line 42: Move the hardcoded 60/min into configuration and add observability:
add a new config field (e.g., Options.FoundLabelRatePerMinute or
FoundLabelRateLimit) and use it when constructing s.foundLabelLimiter via
newSimpleRateLimiter instead of the literal 60; then instrument the rate limiter
rejection path (where handlers return 429) to increment a metric/counter (e.g.,
found_label_rate_limited_total) and expose it to your metrics system so alerts
can be created on spikes; reference s.foundLabelLimiter, newSimpleRateLimiter,
and s.conf.Options.TrustProxy when making the change and update any config
parsing/validation to ensure the value is tunable at runtime if supported.

In `@docs/public/api/openapi-3.0.json`:
- Around line 5394-5404: Update the OpenAPI schema for repo.FoundEntityContact
by adding shape metadata: set "ownerEmail" to include format: "email" and set
"itemId" to an appropriate format (e.g., "uuid" or "int64" per your actual ID
type), and add an "example" object showing a realistic itemId and ownerEmail to
improve generated-client validation and docs; reference the
repo.FoundEntityContact schema and its properties ("ownerEmail", "itemId") when
making these edits. Also, though not a doc change, ensure the implementation of
the unauthenticated endpoint is tracked/fixed to enforce per-IP and global
rate-limiting, sanitize logs to avoid writing returned emails at info level, and
consider returning a relay/tokenized contact instead of raw ownerEmail in a
future iteration.

In `@docs/public/api/openapi-3.0.yaml`:
- Around line 3881-3890: The usergroup.Role enum contains a duplicated value
"user" which can confuse consumers; update the schema so the enum array contains
unique values (e.g., ["user","owner"]) and ensure x-enum-varnames aligns with
those values (e.g., DefaultRole/RoleUser/RoleOwner only if they map to distinct
enum entries), or if the duplicate represents a modeled default, replace the
duplicate with a documented sentinel (or remove the DefaultRole duplicate) and
adjust the generator that produces usergroup.Role so it does not emit duplicate
enum entries; locate this in the OpenAPI definition under the usergroup.Role
schema and update the enum and x-enum-varnames accordingly.

In `@docs/public/api/swagger-2.0.json`:
- Around line 1173-1180: Update the operation responses that currently return
"200" with "$ref": "#/definitions/repo.FoundEntityContact" (i.e., the public
contact/email-revealing endpoints) to include explicit "429" and appropriate
"404"/"422" response entries: add a "429" response with a short description like
"Too Many Requests" and a schema pointing to your existing error model (e.g.,
"#/definitions/Error" or the project's validation/error definition), add a "404"
response with description "Not Found" and an error schema, and add a "422"
response with description "Unprocessable Entity" (validation error) and an
error/validation schema; ensure these new response objects sit alongside the
existing "200" response for the same operation(s) that reference
repo.FoundEntityContact so clients can handle rate-limiting and
missing/validation cases.

In `@docs/public/api/swagger-2.0.yaml`:
- Around line 1990-1999: The OpenAPI enum for usergroup.Role contains a
duplicate "user" entry which can confuse clients; update the schema generation
or the YAML so enum values are unique by removing the duplicate "user" entry (or
post-process the generated enum) and ensure x-enum-varnames still map correctly
(DefaultRole, RoleUser, RoleOwner); also check the similar authroles.Role
generation to apply the same deduplication rule so both enums only list each
value once.

In `@frontend/pages/found/`[kind]/[id].vue:
- Around line 11-13: The page currently uses hardcoded UI strings (e.g. the head
title set via useHead("HomeBox | Found Item"), and the headings, button labels,
and loading/error copy referenced around the noted ranges) — replace all of
those with i18n translation keys by wiring up the vue-i18n APIs (e.g. import/use
useI18n or $t) in the component, change literal strings in the script/template
to calls like t('found.item.title') or $t('found.item.loading'), and add
corresponding keys to the locale files; ensure the useHead title uses the
translated value (t('found.item.title')) and update all button labels, headings,
and loading/error messages to use the new keys (look for uses in the component
methods and template such as useHead, template text nodes, and any button/alert
text).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4e36a329-27c4-46e7-becf-167ddcbbccfa

📥 Commits

Reviewing files that changed from the base of the PR and between 89d4a9b and 941dc35.

⛔ Files ignored due to path filters (5)
  • backend/app/api/static/docs/docs.go is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/openapi-3.json is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/openapi-3.yaml is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/swagger.json is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/swagger.yaml is excluded by !backend/app/api/static/docs/**
📒 Files selected for processing (13)
  • backend/app/api/app.go
  • backend/app/api/handlers/v1/v1_ctrl_found_item.go
  • backend/app/api/routes.go
  • backend/internal/data/repo/repo_entities.go
  • backend/internal/data/repo/repo_entities_found_item_test.go
  • docs/public/api/openapi-3.0.json
  • docs/public/api/openapi-3.0.yaml
  • docs/public/api/swagger-2.0.json
  • docs/public/api/swagger-2.0.yaml
  • frontend/lib/api/public.ts
  • frontend/lib/api/types/data-contracts.ts
  • frontend/middleware/auth.ts
  • frontend/pages/found/[kind]/[id].vue

Comment on lines +756 to +793
"/v1/found/assets/{id}":
get:
tags:
- Entities
summary: Get found asset contact
parameters:
- description: Asset ID
name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/repo.FoundEntityContact"
"/v1/found/entities/{id}":
get:
tags:
- Entities
summary: Get found item contact
parameters:
- description: Entity ID
name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/repo.FoundEntityContact"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

CRITICAL: Public endpoints expose owner email addresses (PII).

These unauthenticated endpoints return ownerEmail (PII) to anyone, creating the same critical security and privacy risks documented in the swagger-2.0.yaml review (lines 2883-2918).

Key risks:

  • Email harvesting via ID enumeration
  • GDPR/CCPA privacy violations
  • No documented anti-scraping measures in the API spec
  • Exposes users to spam and phishing attacks

Required actions:

  • Document rate limiting in the OpenAPI spec
  • Add security warnings and privacy policy references
  • Consider safer alternatives (contact forms, one-time tokens, masked emails)
  • Implement CAPTCHA or proof-of-work challenges
  • Provide user opt-out mechanism

See the detailed security review on swagger-2.0.yaml lines 2883-2918 for full recommendations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/api/openapi-3.0.yaml` around lines 756 - 793, The OpenAPI paths
"/v1/found/assets/{id}" and "/v1/found/entities/{id}" currently expose PII via
the repo.FoundEntityContact schema (ownerEmail); update the spec to eliminate or
mask ownerEmail from that schema (or replace it with a non-PII field like
ownerContactMasked) and add security requirements and rate limiting notes to
both path definitions (e.g., a security requirement referencing existing
securitySchemes and a description of rate-limiting/anti-scraping measures), add
a privacy/security warning and link to the privacy policy in the path summaries
or descriptions, and add a recommended alternative endpoint or mechanism
(contact form/one-time token) in the documentation for contacting owners; ensure
the change updates the components/schemas/repo.FoundEntityContact reference so
no public response returns raw ownerEmail.

Comment on lines +2883 to +2918
/v1/found/assets/{id}:
get:
parameters:
- description: Asset ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/repo.FoundEntityContact'
summary: Get found asset contact
tags:
- Entities
/v1/found/entities/{id}:
get:
parameters:
- description: Entity ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/repo.FoundEntityContact'
summary: Get found item contact
tags:
- Entities
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

CRITICAL: Public endpoints expose owner email addresses (PII).

These endpoints are unauthenticated (no security section) and return ownerEmail, exposing personally identifiable information (PII) to anyone who can access the API.

Security & Privacy Risks:

  1. Email harvesting: Attackers can enumerate IDs to collect email addresses for spam, phishing, or targeted attacks
  2. Privacy violations: Exposing emails without explicit user consent may violate GDPR, CCPA, or other privacy regulations
  3. No anti-scraping measures: The OpenAPI spec shows no CAPTCHA, token requirements, or other anti-automation controls
  4. Minimal rate limiting visibility: While the PR mentions rate limiting, it's not documented in the API specification

Recommendations:

Must-have protections:

  • Document rate limiting in the OpenAPI spec (X-RateLimit headers, limits, etc.)
  • Add privacy policy reference explaining email exposure when generating QR labels
  • Consider requiring user opt-in for public contact availability

Strongly recommended alternatives:

  • Use a contact form/proxy that doesn't expose the raw email address
  • Implement a one-time-use token system instead of direct ID lookups
  • Add CAPTCHA or proof-of-work challenges to prevent automated scraping
  • Return a masked/hashed contact method instead of the raw email

At minimum:

  • Add security warnings to the endpoint documentation
  • Implement aggressive rate limiting (e.g., 5 requests per IP per hour)
  • Log all access attempts for abuse monitoring
  • Provide users a way to disable public contact on their items
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/public/api/swagger-2.0.yaml` around lines 2883 - 2918, The two public
GET paths (/v1/found/assets/{id} and /v1/found/entities/{id}) expose PII via the
repo.FoundEntityContact schema (ownerEmail) and lack any security/rate-limit
documentation; update these endpoint specs to require authentication or a
documented security scheme (add a security: section), remove or replace
ownerEmail in the response schema repo.FoundEntityContact with a non-PII
alternative (e.g., contactProxy, maskedEmail, or token) and document the privacy
implications, add X-RateLimit response header definitions and a brief note
linking to the privacy policy and opt-out instructions, and if you cannot change
behavior, add an explicit warning in the endpoint descriptions that ownerEmail
is PII and rate-limited access/abuse logging is enforced.

Comment thread frontend/lib/api/public.ts
Comment thread frontend/middleware/auth.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⬆️ enhancement New feature or request go Pull requests that update Go code review needed A review is needed on this PR or Issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lost item page if a QR code is scanned but user is not signed in

1 participant