bcbb4469 | WS7: Company Search panel (Companies House) | sloth-ninja | 2026-07-19 | ↗ GitHub |
commit body Backend: extract getCompanyBundle from the chat tool so the new
/companies routes (search, profile+officers+PSCs bundle, paginated
filing history) share one implementation; requireAuth + try/catch on
every handler, fixed friendly error details (401->409 key-missing code,
404/429 mapped, else generic 502); new researchLimiter
(RATE_LIMIT_RESEARCH_WINDOW_MINUTES/RATE_LIMIT_RESEARCH_MAX, documented
in .env.example and CLAUDE.md).
Frontend: Research > Company Search page (320px master list / flex-1
detail, Overview/Officers/PSCs/Filing history tabs per approved
mock-up), reusing PageHeader, TableToolbar, skeletons and the existing
CompanyPanel (new optional `section` prop); new FilingHistoryList
(lazy-fetched, 25/page, DD/MM/YYYY, Companies House register link);
Research sidebar group with Building2 icon; mikeApi chSearchCompanies /
chGetCompany / chGetFilingHistory; sessionStorage assistant-prefill
handoff ("Continue in Assistant" -> ChatInput initialValue).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
e258501a | Docs: BUILD_LOG entry for WS7 PR 2 (Company Search) | sloth-ninja | 2026-07-19 | ↗ GitHub |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
b83a1e4d | WS7 review fixes: company-number validation, profile type guard, key-precedence docs | sloth-ninja | 2026-07-19 | ↗ GitHub |
commit body - /companies routes: reject company numbers that aren't strictly
alphanumeric after normalisation (400, fixed detail) so crafted input
with path/query metacharacters never reaches the Companies House URL;
validateCompanyNumber exported + tested.
- company-search page: guard profile narrowing with typeof "object".
- Docs truth fix: key resolution is env-first with per-user BYO fallback
(userApiKeys.ts seeds env keys first) - corrected CLAUDE.md env
registry, BUILD_LOG wording and the route header comment. Precedence
behaviour itself unchanged (open owner decision, PR #25 review).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
93622ea5 | Merge origin/main (PR #24 Citation Checker) into ws7-company-search | sloth-ninja | 2026-07-19 | ↗ GitHub |
commit body Conflict resolutions (keep-both throughout):
- AppSidebar.tsx: adopted #24's renderNavItem pattern; the Research group
now lists both Company Search and Citation Checker (collapsible variant
from this branch dropped in favour of the merged static group label).
- backend/src/index.ts: both limiters (researchLimiter, citationsLimiter)
and both mounts (/companies, /citations) kept.
- mikeApi.ts: both the Companies House section and checkCitations kept.
- backend/.env.example: both rate-limit var blocks kept.
- CLAUDE.md: both module-map route entries merged into one row;
RATE_LIMIT_* count now 16 covering research + citations vars.
- docs/BUILD_LOG.md: both entries kept, this branch's entry first;
verification updated to the post-merge numbers (vitest 133/133).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
a7b5ede3 | Merge pull request #25 from Sloth-ninja/ws7-company-search | Ezana.H | 2026-07-19 | ↗ GitHub |
WS7: Company Search panel (Companies House) |
70836776 | Company search: surface ceased PSCs/resigned officers + view filing documents | sloth-ninja | 2026-07-28 | ↗ GitHub |
commit body Two owner-reported company-search defects.
Defect 1 (frontend only, CompanyPanel - backs both the assistant side
panel and the /company-search tabs): the PSC renderer never read
`ceased_on`, so ceased PSCs displayed as current; resigned officers only
had inline text. Both now carry a neutral grey "Ceased"/"Resigned" pill
(shared Badge) + DD/MM/YYYY date, a de-emphasised row, active-first stable
ordering, and a muted "· N ceased/resigned" count in the section header.
Defect 2: filing-history rows had no way to open the underlying document.
New backend proxy GET /companies/:companyNumber/filing-history/
:transactionId/document walks the CH Document API chain (transaction →
document_metadata → signed content URL), streaming the PDF inline with a
25 MB guard and a host-check (SSRF defence) on the upstream-supplied URLs;
the key is dropped on the cross-origin 302 to S3 and never logged. Route
uses the sibling try/catch + companiesHouseErrorResponse/logAndRespond
pattern (guarantees a response with fixed safe details - the guarantee the
asyncHandler rule exists to provide). Frontend gains a per-item "View PDF"
affordance (blob → new tab, busy + inline-error states). Chat tool
description now tells the model to flag resigned/ceased status explicitly.
Tests: +6 lib (metadata→content helper: happy chain, 404 no-metadata,
404 no-content-link, oversize reject, off-host SSRF reject, empty-key
401) and +6 route (validateTransactionId + document route over a real
server with the CH client mocked). Backend 385/385 green; tsc + eslint
clean. No new deps, no migrations. UI screenshots pending (headless).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
323dad3b | Company-search saves migration: starred + recent companies table | sloth-ninja | 2026-07-28 | ↗ GitHub |
commit body Owner-authorised 28/07/2026 (allowlist entry added by the owner).
Additive per-user table company_search_saves (hidden_workflows
precedent): starred flag + last_viewed_at recents ordering, RLS
enabled, browser-role grants revoked. schema.sql mirrored. Feature
code follows in a separate PR.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
9d2b0f1c | Merge pull request #49 from Sloth-ninja/company-search-saves-migration | Ezana.H | 2026-07-28 | ↗ GitHub |
Company-search saves migration: starred + recent companies |
aed9d357 | Harden CH filing-document proxy per review (SSRF, size guard, nits) | sloth-ninja | 2026-07-28 | ↗ GitHub |
commit body Addresses independent review (REQUEST_CHANGES) of the filing-document path.
BLOCKER - bypassable host check: the `startsWith(DOCUMENT_API_BASE)` guard
on the metadata/content links passed for suffix-domain
(`...service.gov.uk.evil.com`) and userinfo (`...service.gov.uk@evil.com`)
spoofs; since Authorization is attached to the initial request, a tampered
link would exfiltrate the CH API key and give an SSRF primitive. Replaced
both checks with `isDocumentApiUrl` - `new URL(u)` + exact
`protocol === "https:" && host === "document-api.company-information.service.gov.uk"`.
Added regression tests for both exact attack URLs (rejected 404, no
key-attached fetch).
SHOULD-FIX - size guard buffered before checking: the post-`arrayBuffer`
byteLength check could balloon memory on a lying/chunked response. Now
streams the body with a running byte counter and aborts via AbortController
the moment the 25 MB cap is exceeded (Content-Length stays as a fast path).
Test: a mocked stream exceeding the cap is aborted and maps to the fixed
generic 502.
NITS: (1) allowlist the response Content-Type (application/pdf,
application/octet-stream) - anything else forced to octet-stream so an inline
disposition never renders untrusted upstream content; test added.
(2) FilingHistoryList: handle `window.open` returning null (pop-up blocked) -
revoke the blob URL immediately and show the inline error.
Docs: corrected the DURABLE_LESSONS entry to prescribe URL-parse + exact
protocol/host equality (naming both bypass shapes) instead of startsWith;
BUILD_LOG updated to reflect the hardened guard + streaming + allowlist.
Backend: tsc clean; vitest 388/388 (+3 lib tests). Frontend: tsc + eslint
clean. No new deps; no migrations touched; API key never logged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
d77f585a | Merge remote-tracking branch 'origin/main' into company-search-status-filings | sloth-ninja | 2026-07-28 | ↗ GitHub |
# Conflicts:
# docs/BUILD_LOG.md
|
b7e83974 | Company-search saves: starred + recent companies | sloth-ninja | 2026-07-28 | ↗ GitHub |
commit body Application code for the starred/recent companies feature over the
company_search_saves table (migration #49).
Backend: self-contained lib/companySearchSaves.ts (recordCompanyView with
snapshot refresh + 25-recent prune that never prunes starred rows;
setCompanyStar that inserts a not-yet-saved company from its snapshot;
listCompanySaves), all 42P01/42703-tolerant so an unmigrated production DB
degrades to empty lists / no-ops. Routes on the existing /companies router
(same requireAuth + research limiter, sibling try/catch + fixed generic
detail, caller id from the session): GET /companies/saves,
POST /companies/:companyNumber/view, PUT /companies/:companyNumber/star, with
exported validateViewBody / validateStarBody body validation.
Frontend: company-search rail shows Starred then Recent sections (star toggle
per row) when the search box is empty, a header star toggle for the open
company, best-effort fire-and-forget view recording on open, and optimistic
star updates with rollback. mikeApi getCompanySaves/recordCompanyView/
setCompanyStar + types.
Tests: +20 backend (upsert/snapshot/prune, star/unstar incl. insert-with-
snapshot, cross-user isolation, 42P01/42703 degradation, route validation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
9d2b1098 | Merge pull request #48 from Sloth-ninja/company-search-status-filings | Ezana.H | 2026-07-28 | ↗ GitHub |
Company search: surface ceased PSCs/resigned officers + view filing documents |
89de3ae8 | Merge remote-tracking branch 'origin/main' into company-search-saves | sloth-ninja | 2026-07-28 | ↗ GitHub |
# Conflicts:
# backend/src/routes/companies.test.ts
# backend/src/routes/companies.ts
# docs/BUILD_LOG.md
# frontend/src/app/lib/mikeApi.ts
|
a7dcef61 | Merge pull request #51 from Sloth-ninja/company-search-saves | Ezana.H | 2026-07-28 | ↗ GitHub |
Company-search saves: starred + recent companies |
cd346c7e | Company-search train: composed-range fix wave | sloth-ninja | 2026-07-28 | ↗ GitHub |
commit body Four cross-module findings from the composed-range review of the
company-search train (none catchable per-PR):
1. Right-to-erasure gap: deleteUserAccountData now deletes the user's
company_search_saves rows (42P01/42703-tolerant so a self-hosted DB that
has not run migration 20260728_01 does not fail account deletion). Saves
are user-level, so project cleanup paths are untouched.
2. SAR completeness: buildUserAccountExport now includes company_search_saves
(all columns; missing table -> empty section via a tolerateMissing option
on selectAll).
3. Star-error visibility: surface the fixed star-failed message under the
detail-header StarToggle on company-search (not only in the empty rail).
4. Copy honesty: land-registry Price Paid footnote reports the real count
("Showing all N transaction(s)." / capped-at-25 wording) instead of a
hardcoded "25 most recent".
The 42P01/42703 idiom is shared, not duplicated: isMissingTableOrColumn is
exported from companySearchSaves.ts. New backend tests cover deletion +
export inclusion and the missing-table tolerance (+7). Backend tsc clean,
vitest 442 passed; frontend tsc + eslint clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
27f51e8b | Merge pull request #52 from Sloth-ninja/company-search-train-fixes | Ezana.H | 2026-07-28 | ↗ GitHub |
Company-search train: composed-range fix wave |
33a7eb26 | Company-search train close-out: status refresh | sloth-ninja | 2026-07-28 | ↗ GitHub |
commit body CLAUDE.md Current status updated: #48-#52 merged, deployed and
verified live; 20260727_01 run in production (firm features active);
20260728_01 saves migration still owner-pending in prod; Quill
capability verification recorded, spike gated on QUILL_DOMAIN.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
02c69cf4 | Merge pull request #53 from Sloth-ninja/company-search-closeout | Ezana.H | 2026-07-28 | ↗ GitHub |
Company-search train close-out: status refresh |