1
0

942 Commits

Author SHA1 Message Date
Ahmed Bouhuolia 7f3fbdc57d Merge pull request #1095 from bigcapitalhq/fix/plaid-webhook-signature-verification
fix(server): verify Plaid webhook signatures (GHSA-g56w-g54f-whq5)
2026-05-17 23:04:30 +02:00
Ahmed Bouhuolia 1a4f2e5b5d wip 2026-05-17 21:12:31 +02:00
Ahmed Bouhuolia 19d8aec2aa Merge remote-tracking branch 'refs/remotes/origin/feat/financial-audit-trail' into feat/financial-audit-trail 2026-05-17 20:46:03 +02:00
Ahmed Bouhuolia 8c8e5138df wip 2026-05-17 20:38:56 +02:00
Ahmed Bouhuolia d74e02c21a Merge branch 'develop' into feat/financial-audit-trail 2026-05-17 19:55:15 +02:00
Ahmed Bouhuolia 00feae58a7 wip 2026-05-17 19:50:00 +02:00
Ahmed Bouhuolia c23bc76afa fix(server): prevent SQL injection via sortOrder in DynamicListing (GHSA-hcp2-qqg6-jjpm)
Validate sortOrder against an allowlist at the DTO layer, normalize the
direction centrally in DynamicFilterSortBy.buildQuery, and re-sanitize
inside every orderByRaw modifier so attacker-controlled SQL cannot reach
the ORDER BY clause.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:35:22 +02:00
Ahmed Bouhuolia ace15dbdeb fix(server): use CSPRNG for attachment S3 keys (GHSA-gj48-p5ff-g67f)
The multer-s3 storage factory used `Date.now().toString()` as the S3 key
for every upload, yielding a 13-digit ms-epoch key. The keyspace for any
time window equals the millisecond count of that window, so an attacker
holding a registered account can enumerate keys for known upload moments
(e.g. ~10 minutes for a 10-second window with a 10-proxy rotation), then
download files via `GET /attachments/:id/presigned-url`. Two uploads in
the same millisecond also collide and silently overwrite each other.

Replace the key callback with `${organizationId}/${randomUUID()}`:

  * `randomUUID()` from `node:crypto` is a v4 UUID with 122 bits of
    entropy, making brute-force enumeration infeasible.
  * The `<organizationId>/` prefix (read from the `nestjs-cls` store
    populated by `ClsModule` middleware in `App.module.ts`) limits the
    blast radius of any hypothetical bucket-listing leak to a single
    tenant.

Add a tenant migration applying `unique` to `documents.key` so any future
key collision surfaces as a DB error instead of a silent S3 overwrite.

Legacy 13-digit numeric keys remain accessible via their stored values;
only new uploads use the new format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 19:38:39 +02:00
Ahmed Bouhuolia 78fb158b98 fix(server): verify Plaid webhook signatures (GHSA-g56w-g54f-whq5)
POST /api/banking/plaid/webhooks was @PublicRoute() and processed the
body without verifying Plaid's Plaid-Verification JWT, letting any
unauthenticated client replay or fabricate webhook events for a tenant
by guessing a plaidItemId.

Add PlaidWebhookVerificationService that verifies the Plaid-Verification
ES256 JWS using a JWK fetched from plaidClient.webhookVerificationKeyGet
(cached per kid via lru-cache for 24h), enforces a 5-minute iat replay
window through jose.jwtVerify({ maxTokenAge }), and timing-safe compares
the body's SHA-256 against the request_body_sha256 claim. The webhook
controller now consumes the raw body and the plaid-verification header,
runs verification before setupPlaidTenant, and returns 400 Bad Request
on any failure - so no tenant context is ever set for an unsigned or
tampered request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:19:30 +02:00
Ahmed Bouhuolia 7efac090a9 fix(server): prevent cross-tenant access via organization-id header
Resolve a CLS middleware in App.module.ts to copy the request
`organization-id` header straight into `cls.organizationId`, which the
TenancyDB factory used to pick the per-tenant database. The JWT path
never set `organizationId` from the authenticated user, and
TenancyGlobalGuard only checked that the header was present — so any
authenticated user could read or write another tenant's database by
sending their own JWT plus the victim's `organization-id`.

Make the JWT-resolved tenant the source of truth and validate the
header at the edge:

- AuthSigninService.verifyPayload now loads the user's tenant and sets
  `cls.organizationId` from `tenant.organizationId`, mirroring the
  API-key path in AuthApiKeyAuthorizeService.
- TenancyGlobalGuard rejects with `Organization mismatch.` when the
  request header disagrees with the CLS value set by the auth guard.
- App.module.ts no longer seeds `cls.organizationId` from the
  attacker-controlled request header.

GHSA-2g96-86rw-qmvg

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 20:43:14 +02:00
Ahmed Bouhuolia a9d2316fc2 Merge pull request #1093 from bigcapitalhq/fix/attachment-tenant-isolation
fix(server): prevent cross-tenant attachment access (IDOR)
2026-05-15 14:54:19 +02:00
Ahmed Bouhuolia e18e61000d fix(server): prevent cross-tenant attachment access (IDOR)
Add tenant-scoped document lookup with throwIfNotFound() before S3
operations in GetAttachment, DeleteAttachment, and
GetAttachmentPresignedUrl services. This prevents users from reading,
deleting, or generating presigned URLs for attachments belonging to
other tenants.

Also adds RequirePermission decorators to the three attachment
endpoints and introduces Attachment ability subject with View and
Delete actions.

GHSA-rc4v-wq22-v6cf

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 14:47:18 +02:00
Ahmed Bouhuolia f8afa00f29 Merge pull request #1089 from c-premus/fix/dynamic-filter-pagination 2026-05-12 10:53:54 +02:00
rebekah-create 3cbc4842b9 fix(server): correct "Liabilties" typo to "Liabilities" in balance sheet
The balance sheet rendered "Current Liabilties" in the Liabilities
section because of a typo in the i18n key, the schema reference, the
swagger example responses, and the generated SDK fixtures. Fixed all
five locations so PDF/HTML/JSON renders all read "Current Liabilities".

- packages/server/src/i18n/en/balance_sheet.json: rename key and value
- packages/server/src/modules/FinancialStatements/modules/BalanceSheet/
  BalanceSheetSchema.ts: update i18n key reference
- packages/server/src/modules/FinancialStatements/modules/BalanceSheet/
  BalanceSheet.swagger.ts: fix three example response strings
- shared/sdk-ts/openapi.json + schema.ts: regenerate to match
2026-05-12 00:10:44 -04:00
Chris 46012a1b1c Fix pagination params silently ignored on collection GET endpoints
Hoist `page` and `pageSize` declarations from the per-module DTOs into
the shared DynamicFilterQueryDto base class. Without these declarations,
the global ValidationPipe (whitelist: true) strips the params from the
request before the service layer sees them, so list services fall back
to their default page=1, pageSize=12 regardless of what the client sent.

Affects 10 collection GET endpoints whose query DTOs are empty subclasses
of DynamicFilterQueryDto: expenses, bills, credit-notes, manual-journals,
payments-received, sale-invoices, sale-estimates, sale-receipts,
vendor-credits, item-categories.

The 3 already-working DTOs (Customers, Vendors, Items) keep their local
page/pageSize declarations as redundant overrides — no behavior change.

Closes #1088
2026-04-29 11:51:49 +00:00
Ahmed Bouhuolia 52c97f1401 Merge pull request #1075 from bigcapitalhq/fix/export-i18n-column-headers
fix(server): localize i18n column headers in CSV/XLSX/PDF exports
2026-04-13 18:37:38 +02:00
Ahmed Bouhuolia 3db9061c60 fix(server): localize i18n column headers in export (CSV/XLSX/PDF)
Export column headers displayed raw i18n keys like
`expense.field.payment_account` instead of translated names like
"Payment Account" because ExportResourceService never resolved the
i18n keys before rendering.

Inject I18nService and translate column names in both
getExportableColumns() (CSV/XLSX) and getPrintableColumns() (PDF).

Closes #1073

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 18:32:24 +02:00
Ahmed Bouhuolia a7a0a7fbe7 fix(server): use correct base currency in financial reports
Reports (Receivable Aging Summary, Payable Aging Summary, Inventory
Valuation, Sales Tax Liability Summary, Vendor Balance Summary) were
not assigning baseCurrency from meta in their constructors, causing
currency formatting to fall back to USD instead of the organization's
base currency.

Closes #1069

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 17:52:46 +02:00
Y8C68 dd58f50387 fix(server): handle missing ContentType in attachment download
GET /api/attachments/:id crashes with "Cannot read properties of
undefined (reading extension)" when the S3 object has no ContentType
metadata. This happens when files are uploaded without explicit content
type (e.g., via API integrations).

mime.extension(undefined) returns undefined, which then causes the
Content-Disposition header template to fail.

Fix: fallback to "application/octet-stream" when ContentType is missing,
and "bin" when mime.extension() returns undefined.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 06:11:28 +00:00
Ahmed Bouhuolia d5cc3d1a71 wip 2026-03-27 11:29:57 +02:00
Ahmed Bouhuolia 2e90e3cc5b wip 2026-03-26 18:04:33 +02:00
Ahmed Bouhuolia 8b59f28125 wip 2026-03-26 17:40:24 +02:00
Ahmed Bouhuolia cfbfc0b746 fix(accounts): correct typos in account type constants (#1046)
- Fix 'none-current-asset' -> 'non-current-asset' in ACCOUNT_TYPE
- Fix 'non-ACCOUNT_PARENT_TYPE.CURRENT_ASSET' -> 'non-current-asset' copy-paste error
- Fix 'expene' -> 'expense' typo in ACCOUNT_ROOT_TYPE
- Add database migration to update existing records

Fixes #1041

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 02:34:41 +02:00
Ahmed Bouhuolia 5caa4bce61 Merge pull request #1045 from bigcapitalhq/self-contained-e2e-github-action
feat(ci): self contained e2e GitHub action
2026-03-15 23:49:49 +02:00
Ahmed Bouhuolia 9b31210f6d wip 2026-03-15 21:14:27 +02:00
Ahmed Bouhuolia 2f45263263 feat(server): add Query DTOs for consistent filtering across modules
- Add GetBillsQuery.dto.ts, GetCreditNotesQuery.dto.ts, GetExpensesQuery.dto.ts
- Add GetItemCategoriesQuery.dto.ts, GetManualJournalsQuery.dto.ts
- Add GetPaymentsReceivedQuery.dto.ts, GetSaleEstimatesQuery.dto.ts
- Add GetSaleInvoicesQuery.dto.ts, GetSaleReceiptsQuery.dto.ts, GetVendorCreditsQuery.dto.ts
- Update DynamicFilterQuery.dto.ts with enhanced filter options
- Refactor controllers and services to use new Query DTOs
- Update SDK schema and sale-estimates types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 08:50:15 +02:00
Ahmed Bouhuolia 3706e048b6 refactor(server): migrate ExchangeRates module to NestJS
- Convert TypeDI services to NestJS @Injectable() pattern
- Replace Express router with NestJS @Controller() decorators
- Migrate express-validator to class-validator DTOs
- Add Swagger/OpenAPI documentation decorators
- Fix import paths for TenantMetadata and ServiceError
- Add ExchangeRatesModule to AppModule imports
2026-03-14 05:16:06 +02:00
Ahmed Bouhuolia 06d1cf3119 feat: add response DTOs for CreditNoteRefunds and Resource modules
- Add ResourceMetaResponse DTO for resource metadata
- Update CreditNoteRefunds service with proper types
- Regenerate SDK types from updated OpenAPI schema
- Update SDK bank-rules, credit-notes, and organization modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 23:19:22 +02:00
Ahmed Bouhuolia 48e41da345 Merge pull request #1033 from bigcapitalhq/feat/credit-note-sdk-ts-utils
feat: add response DTOs for credit note modules and SDK types
2026-03-09 07:18:36 +02:00
Ahmed Bouhuolia b59f40d295 feat: add response DTOs for credit note modules and SDK types 2026-03-09 07:12:10 +02:00
Ahmed Bouhuolia ee2726c0c7 feat(server): add response DTOs for Payment Links, Stripe, Credit Notes and Inventory Cost 2026-03-09 06:35:02 +02:00
Ahmed Bouhuolia 96338cc215 chore(docker): upgrade pnpm version in Dockerfiles for server and webapp
- Updated pnpm from version 8.10.2 to 9.0.5 in both server and webapp Dockerfiles to ensure compatibility with the latest features and improvements.
2026-03-08 04:55:16 +02:00
Ahmed Bouhuolia 400fec3e98 refactor(docker): simplify Dockerfile by consolidating shared package copies
- Updated Dockerfiles for server and webapp to copy all shared packages in a single command, improving maintainability.
- Removed individual package copy commands for bigcapital-utils, pdf-templates, and email-components.
- Ensured that all shared packages are included automatically during the build process.
2026-03-08 04:49:10 +02:00
Ahmed Bouhuolia 8dd895b34f feat: add credit note status translations and manual journal default views
- Add credit note view status translations (draft, published, open, closed)
- Add Manual Journal default views for draft and published statuses
- Add DEFAULT_VIEW_COLUMNS constant for reusability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 21:08:48 +02:00
Ahmed Bouhuolia 6d1e93e84b fix(accounts): add missing dynamic list filter properties to GetAccountsQueryDto
Add missing viewSlug, filterRoles, stringifiedFilterRoles, searchKeyword,
columnSortBy, sortOrder, customViewId, page, and pageSize properties to
GetAccountsQueryDto to enable proper filtering when selecting table views
(Assets, Liabilities, Equity, Income, Expenses) on the Accounts Chart page.

Previously, the API received view_slug but didn't process it because the
DTO lacked these properties, causing all accounts to be returned instead
of filtering by the view's root_type.

Fixes #1023

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 20:23:49 +02:00
Ahmed Bouhuolia e02e143157 fix(payment-received): allow decimal amounts in paymentAmount validation
Change @IsInt() to @IsNumber() for paymentAmount field in PaymentReceivedEntryDto
to allow recording payments with cents (e.g., $1,679.80).

Fixes #1016

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 06:31:55 +02:00
Ahmed Bouhuolia 7dd08d6141 feat(financial-statements): add new financial report DTOs and update controllers
- Introduced new DTOs for various financial reports including Balance Sheet, Cash Flow Statement, and Aging Summaries.
- Updated existing controllers to utilize the new DTOs and enhance OpenAPI documentation with proper schema references.
- Removed unnecessary query parameters from the Bank Accounts controller.
- Enhanced response structures for better data representation in reports.
2026-03-06 05:25:44 +02:00
Ahmed Bouhuolia 631df56cee feat(sdk): add banking and vendor credits SDK utilities
- Add SDK types for bank rules, cashflow accounts, and vendor credits
- Update banking controllers with proper OpenAPI annotations
- Update vendor credits controllers with new endpoints and DTOs
- Enhance banking transaction handling for categorize, recognize, pending, and exclude operations
- Add vendor credit apply bills and refund functionality

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 23:06:44 +02:00
Ahmed Bouhuolia 4c059d610e feat(sdk-ts): add authentication fetch utils 2026-03-05 19:50:38 +02:00
Ahmed Bouhuolia e3d3da7cd9 feat(sdk): move the generate sdk ts types to nestjs command 2026-03-04 00:20:46 +02:00
Ahmed Bouhuolia e3c55c5d6f feat(sdk): add OpenAPI export script and TypeScript SDK package
- Add export-openapi.ts script for server OpenAPI spec export
- Add shared/sdk-ts package with generated API clients (accounts, bills, customers, vendors, etc.)
- Update Customers and Vendors controllers
- Update ReportsEventsTracker
- Update .gitignore, package.json, and pnpm-lock

Made-with: Cursor
2026-03-03 23:26:24 +02:00
Ahmed Bouhuolia 28786712ea fix(server): Fix UnlinkAttachment model reference bug
Add missing function invocation on LinkModel to properly call query method.
The model reference was missing parentheses to invoke the factory function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:35:21 +02:00
Ahmed Bouhuolia b98485c5f3 fix(server): PDF template logo not showing on reopen
Generate presigned URL for companyLogoKey when retrieving PDF template
to allow the logo to display when reopening the branding template drawer.

- Inject GetAttachmentPresignedUrl service in GetPdfTemplateService
- Generate companyLogoUri from companyLogoKey in template attributes
- Add companyLogoUri to transformer included attributes

Fixes the issue where logo uploads and saves but doesn't show when
reopening the branding template customization drawer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 05:22:01 +02:00
Ahmed Bouhuolia 3575d54efa fix: add attachment support for all transaction types
Fixed attachments not showing in edit forms for various transaction types by:

1. Adding @InjectAttachable() decorator to models:
   - SaleReceipt, SaleEstimate, CreditNote, PaymentReceived
   - Bill, BillPayment, VendorCredit
   - ManualJournal, Expense

2. Fixing transformers to include attachments in API responses:
   - SaleReceiptTransformer, PaymentReceivedTransformer

3. Registering missing event subscribers in Attachment.module.ts:
   - AttachmentsOnSaleReceipts, AttachmentsOnSaleEstimates

4. Fixing DocumentLink model relation mapping:
   - Changed Document.default to Document for proper module export

5. Fixing PaymentReceived model_ref consistency:
   - Changed from 'PaymentReceive' to 'PaymentReceived' to match class name

6. Adding missing withGraphFetched('attachments') to GetPaymentReceived.service.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 23:16:12 +02:00
Ahmed Bouhuolia ad252d2e4a fix(models): remove timestamps from models where tables lack createdAt/updatedAt columns
Add withDateSessionMixin for proper timestamp handling and fix models
to return empty timestamps array when database tables don't have
created_at/updated_at columns. This prevents ORM insert/update errors.

Models updated:
- Branch, Role, RolePermission, ViewColumn, ViewRole
- InventoryAdjustment, InventoryAdjustmentEntry, InventoryTransactionMeta
- BillLandedCostEntry, CreditNote, CreditNoteAppliedInvoice, RefundCreditNote
- PaymentReceived, SaleInvoice, SaleReceipt, Item, ItemEntry
- RefundVendorCredit, VendorCreditAppliedBill
- ItemWarehouseQuantity, Warehouse, WarehouseTransfer, WarehouseTransferEntry
- Setting, TenantMetadataModel, TenantUser

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 05:57:55 +02:00
Ahmed Bouhuolia 558fc29962 fix: use organization date format in banking transactions and reports
- Add OrganizationSettingsModule to BankingTransactionsModule
- Update GetBankAccountTransactions to pass dateFormat from settings
- Add meta support to FinancialSheet base class
- Refactor TransactionsByReference to use IFinancialReportMeta
- Update frontend to use server-provided formatted_date
2026-02-25 20:33:31 +02:00
Ahmed Bouhuolia d35915b16b feat(accounts): add account settings service
- Add AccountsSettingsService for managing account-related settings
- Update validators, create and edit services to use settings
- Add constants for account configuration
- Update frontend utils and translations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 19:27:53 +02:00
Ahmed Bouhuolia f5e74f3e88 fix(inventory): update baseCurrency retrieval in InventoryDetailsService
- Replace tenantMetadata.baseCurrency with meta.baseCurrency in InventoryDetailsService to ensure consistent currency usage across reports.
2026-02-25 07:10:09 +02:00
Ahmed Bouhuolia c83132b867 fix(financial-statements): use stored date format settings in all reports
- Replace hardcoded date formats ('YYYY/MM/DD') in all Meta classes with meta.dateFormat
- Add IFinancialReportMeta interface with baseCurrency and dateFormat fields
- Add DEFAULT_REPORT_META constant with default date format 'YYYY MMM DD'
- Update all sheet classes to accept IFinancialReportMeta parameter
- Update all services to pass dateFormat from meta to sheet constructors
- Fix customer/vendor transactions date formatting in table rows
- Add TenancyModule to SalesTaxLiabilityModule for dependency injection
- Make dateFormat optional in JournalSheet and GeneralLedger query types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 07:05:31 +02:00
Ahmed Bouhuolia f35e85c3d2 fix(organization): align date formats and fix address field naming
- Fix date format mismatch between Miscellaneous and Organization constants
- Fix default date format casing ('DD MMM yyyy' -> 'DD MMM YYYY')
- Rename address fields from address_1/address_2 to address1/address2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 22:42:29 +02:00