Skip to content

feat(sap): add SAP Concur integration block and SAP S/4HANA validation fixes#4483

Merged
waleedlatif1 merged 13 commits intostagingfrom
waleedlatif1/sap-concur
May 7, 2026
Merged

feat(sap): add SAP Concur integration block and SAP S/4HANA validation fixes#4483
waleedlatif1 merged 13 commits intostagingfrom
waleedlatif1/sap-concur

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • New SAP Concur block with 70 tools across Expense Reports, Expenses, Receipts, Travel Requests, Itineraries, Attendees, Cash Advances, Budgets, Allocations, Users (Identity v4.1), Lists, Purchase Requests, Exchange Rates, and Exceptions — all validated against live Concur API docs
  • OAuth 2.0 proxy with datacenter routing (US/EU/CN) and SSRF-safe outbound requests
  • SAP S/4HANA fixes from live OData spec validation: removed phantom fields on A_PurchaseRequisitionHeader outputs, removed phantom to_Text expand on A_BillingDocument, corrected several response envelope and field nullability declarations across BP / Customer / Supplier / Product / Sales Order / Delivery / Material / Billing groups, fixed Cash Advance API version paths

Type of Change

  • New feature (SAP Concur)
  • Bug fix (SAP S/4HANA spec alignment)

Testing

Tested manually. Live-API-docs validation pass against developer.concur.com and SAP help.sap.com OData specs across all tool groups.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment May 7, 2026 2:35am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 7, 2026

PR Summary

Low Risk
Low risk docs/UI-only changes that mainly add static MDX content and an SVG icon, with minimal impact outside docs navigation/mapping.

Overview
Adds a new SAP Concur docs entry: a dedicated sap_concur.mdx page documenting the Concur toolset and a corresponding sap_concur entry in tools meta.json so it appears in docs navigation.

Introduces a SapConcurIcon SVG and wires it into the generated blockTypeToIconMap as sap_concur so the new block renders with the correct icon.

Reviewed by Cursor Bugbot for commit e10ab76. Configure here.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/docs/content/docs/en/tools/sap_concur.mdx
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR introduces a new SAP Concur integration block with 70 tools spanning Expense Reports, Travel Requests, Receipts, Cash Advances, Users, Budgets, and more, plus targeted spec-alignment fixes for the existing SAP S/4HANA block. The Concur integration adds a dedicated OAuth 2.0 proxy with per-datacenter routing, DNS-pinned SSRF-safe outbound fetches, and a separate multipart upload endpoint for receipt images.

  • SAP Concur block: Adds apps/sim/app/api/tools/sap_concur/{proxy,upload}/route.ts backed by shared.ts with token caching, datacenter allow-list enforcement, and robust path validation; 70+ tool files with consistent user-only credential visibility; a 1,900-line block definition with conditional field sets for all 14 tool groups.
  • SAP S/4HANA fixes: Removes phantom OData fields and incorrect expands; joinSetCookies now uses response.headers.getSetCookie() (array) instead of a comma-split regex to prevent cookie mangling on CSRF-protected mutations.
  • Shared infrastructure: SecureFetchHeaders.getSetCookie() added to input-validation.server.ts; the Zod contract baseline bumped from 733 to 735 for the two new Concur API routes.

Confidence Score: 5/5

Safe to merge. The Concur proxy and upload routes enforce SSRF protections at multiple layers, credentials are never leaked, and the S/4HANA cookie fix correctly resolves a previously flagged issue.

Both new API routes use internal auth gating, Zod schema validation, allowed-datacenter enforcement, and DNS-pinned outbound fetches. The upload route validates MIME types per operation. All 70 tools mark credentials as user-only. The S/4HANA Set-Cookie fix is minimal and targeted.

No files require special attention. The 130-file changeset is large but mostly repetitive tool definitions following a consistent pattern verified in the proxy and shared modules.

Important Files Changed

Filename Overview
apps/sim/app/api/tools/sap_concur/shared.ts Core OAuth token acquisition, caching, and SSRF guard logic. Token cache is LRU-bounded (500 entries), cache keys include hashed clientSecret and username; geolocation response validated against SAP_CONCUR_ALLOWED_DATACENTERS. Correct and secure.
apps/sim/app/api/tools/sap_concur/proxy/route.ts Concur REST proxy: validates internal auth, parses body through SapConcurProxyRequestSchema, fetches access token, builds URL from validated geolocation and path, calls Concur via secureFetchWithValidation. Error handling is well-structured.
apps/sim/app/api/tools/sap_concur/upload/route.ts Handles multipart receipt uploads; validates MIME type per-operation (RECEIPT vs QUICK_EXPENSE allowed sets). FormData serialization via dummy Request is a reasonable workaround for secureFetchWithValidation compatibility.
apps/sim/app/api/tools/sap_s4hana/proxy/route.ts joinSetCookies now uses response.headers.getSetCookie() (string[]) fixing CSRF cookie mangling for Base64/equal-sign-containing cookie values.
apps/sim/lib/core/security/input-validation.server.ts Adds SecureFetchHeaders.getSetCookie() returning clean string[] of raw Set-Cookie values so downstream callers avoid regex-based cookie splitting.
apps/sim/blocks/blocks/sap_concur.ts 1,919-line block definition; 6 contextType inputs share the same id with different condition arrays and option sets. Large switch dispatch is correct.
apps/sim/tools/sap_concur/utils.ts Shared utilities: baseProxyBody, buildListQuery, transformSapConcurProxyResponse, trimRequired. All straightforward.
apps/sim/tools/sap_concur/upload_receipt_image.ts Correctly routes to /api/tools/sap_concur/upload with operation: upload_receipt_image; credential params marked user-only.

Sequence Diagram

sequenceDiagram
    participant Client as Sim Workflow
    participant Proxy as /api/tools/sap_concur/proxy
    participant Upload as /api/tools/sap_concur/upload
    participant Shared as shared.ts (token cache)
    participant Concur as SAP Concur API

    Client->>Proxy: "POST {auth, path, method, query, body}"
    Proxy->>Proxy: checkInternalAuth()
    Proxy->>Proxy: SapConcurProxyRequestSchema.parse()
    Proxy->>Shared: fetchSapConcurAccessToken(auth)
    alt cache miss
        Shared->>Concur: POST /oauth2/v0/token
        Concur-->>Shared: "{access_token, geolocation}"
        Shared->>Shared: validate geolocation in SAP_CONCUR_ALLOWED_DATACENTERS
        Shared->>Shared: rememberToken() LRU cache
    end
    Shared-->>Proxy: "{accessToken, geolocation}"
    Proxy->>Concur: secureFetchWithValidation DNS-pinned
    Concur-->>Proxy: response
    Proxy-->>Client: "{success, output}"

    Client->>Upload: "POST {auth, operation, userId, receipt}"
    Upload->>Upload: inferMimeType + allowedForOperation check
    Upload->>Shared: fetchSapConcurAccessToken(auth)
    Shared-->>Upload: "{accessToken, geolocation}"
    Upload->>Concur: secureFetchWithValidation multipart POST
    Concur-->>Upload: 202 + Location/Link headers
    Upload-->>Client: "{success, output}"
Loading

Reviews (2): Last reviewed commit: "fix(sap-concur): rename misleading excha..." | Re-trigger Greptile

Comment thread apps/sim/app/api/tools/sap_concur/shared.ts
Comment thread apps/sim/app/api/tools/sap_concur/shared.ts
Comment thread apps/sim/app/api/tools/sap_s4hana/proxy/route.ts
SecureFetchHeaders previously collapsed multi-value Set-Cookie headers
with ", ", forcing consumers to re-split via a fragile regex. Cookie
values containing "=" or "," (e.g., Base64 session tokens) could be
misparsed and produce malformed Cookie strings on CSRF-protected
mutations.

Add SecureFetchHeaders.getSetCookie() that returns the raw array, and
update the S/4HANA OData proxy's joinSetCookies to consume it directly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…refresh_token grant, validate geolocation host

- Rename sap_concur_get_exchange_rate to sap_concur_upload_exchange_rates (POST bulk upload, not GET)
- Remove refresh_token from SapConcurGrantType / Zod enum / block dropdown / docs (no implementation)
- Validate Concur geolocation hostname against SAP_CONCUR_ALLOWED_DATACENTERS

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e10ab76. Configure here.

Tool and trigger descriptions can contain URL path placeholders like
{reportId} or JSON-shape hints like { Items, NextPage }. When rendered
as MDX prose (not table cells), these were emitted unescaped and MDX
parsed them as JSX expressions, failing prerender with
"ReferenceError: reportId is not defined".

Escape { and } in the operation-level description and trigger
description renderers, matching the existing escaping in table-cell
descriptions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…and context types

- list_travel_profiles_summary: rename Status query to Active with 1/0 values, tighten LastModifiedDate format hint
- list_itineraries / get_itinerary: use documented userid_type / userid_value / ItemsPerPage / Page query keys
- create_report_comment: contextType allows MANAGER (move to EXPENSE_READ_CONTEXT_TYPE_OPS)
- get_list_item: drop unused listId from block (tool only needs itemId)
- Tighten description copy on list_expenses/get_itemizations/associate_attendees/remove_all_attendees

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
waleedlatif1 and others added 2 commits May 6, 2026 19:21
- Update Cash Advance create/get/issue tools from /cashadvance/v4/ to /cashadvance/v4.1/ to match the live API
- Add filter query param to list_users (SCIM v4.1 supports filtering by userName, employeeNumber, externalId)
- Regenerate docs MDX

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…v4.1 GET)

SCIM Identity v4.1 GET /Users does not accept a filter query parameter — filtering
is only supported via POST /Users/.search (already exposed by sap_concur_search_users).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
waleedlatif1 and others added 2 commits May 6, 2026 19:25
Verified against live SAP Concur docs (concur/developer.concur.com preview branch):

- Revert Cash Advance paths to /cashadvance/v4/ (v4.1 endpoints do not exist; live spec is v4)
- Travel Profile v2 summary has no Active/Status query param — drop the filter from tool, types, and block
- Report Comments v4 contextType is TRAVELER or PROXY only (NOT MANAGER) — move create_report_comment + list_report_comments into the TRAVELER/PROXY context group
- Trip v1.1 query keys: userid_type / userid_value / ItemsPerPage / Page (snake/Pascal per docs) — already correct, kept

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-verified against live developer.concur.com docs at /api-reference/cash-advance/v4-1.cash-advance.html — only v4.1 endpoints are documented:
- POST /cashadvance/v4.1/cashadvances
- GET /cashadvance/v4.1/cashadvances/{cashAdvanceId}
- POST /cashadvance/v4.1/cashadvances/{cashAdvanceId}/issue

The /cashadvance/v4/ docs page returns 404. Reverts the prior local rollback in 9ef3a11.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@waleedlatif1 waleedlatif1 merged commit a251e45 into staging May 7, 2026
2 of 3 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/sap-concur branch May 7, 2026 02:32
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.

1 participant