Files
README-first/.cursor/rules/endorser-service.mdc

247 lines
8.3 KiB
Plaintext

---
globs: repos/endorser-ch/src/**
alwaysApply: false
---
# Endorser-ch Development Conventions
# Pagination Conventions
### Standard Pagination Parameters
### Required Pattern
- **`beforeId`**: JWT ID to end before (exclusive) - primary pagination parameter
- **`afterId`**: JWT ID to start after (exclusive) - used for feed-style results where newer items are needed
### Parameter Usage
- **Routes**: Accept `beforeId` and `afterId` as query parameters
- **Database methods**: Pass these parameters through to database layer
- **Never use**: `limit`, `offset`, `page`, `skip`, or similar traditional pagination
### Limit
- The limit is managed by the DB service, currently always set to DEFAULT_LIMIT
## API Route Implementation
### Query Parameter Handling
```javascript
// ✅ CORRECT - Use beforeId/afterId pattern
const beforeId = req.query.beforeId
const afterId = req.query.afterId
dbService.methodByParamsPaged(params, afterId, beforeId)
// ❌ WRONG - Never use limit/offset
const limit = parseInt(req.query.limit) || 50 // DON'T DO THIS
const offset = parseInt(req.query.offset) || 0 // DON'T DO THIS
```
### JSDoc Documentation Pattern
```javascript
/**
* @param {string} beforeId.query.optional - the JWT ID of the entry before which to look (exclusive); by default, the last one is included
* @param {string} afterId.query.optional - the JWT ID of the entry after which to look (exclusive); by default, the first one is included
*/
```
## Database Service Implementation
### Method Signature Pattern
```javascript
// ✅ CORRECT - Standard pagination signature
methodByParamsPaged(params, afterIdInput, beforeIdInput) {
// Implementation using JWT ID comparisons
}
// ❌ WRONG - Never use limit/offset in method signatures
methodByParamsPaged(params, limit, offset) { // DON'T DO THIS
```
### SQL Implementation
```javascript
// ✅ CORRECT - Use JWT ID comparisons
let sql = "SELECT * FROM table WHERE conditions"
if (afterIdInput) {
params.push(afterIdInput)
sql += " AND jwtId > ?"
}
if (beforeIdInput) {
params.push(beforeIdInput)
sql += " AND jwtId < ?"
}
sql += " ORDER BY jwtId DESC LIMIT " + DEFAULT_LIMIT
// ❌ WRONG - Never use OFFSET
sql += " LIMIT ? OFFSET ?" // DON'T DO THIS
```
### Response Format
```javascript
// ✅ CORRECT - Standard response format
const result = {
data: processedResults,
hitLimit: results.length === DEFAULT_LIMIT
}
// ❌ WRONG - Don't include limit/offset metadata
const result = {
data: results,
limit: 50, // DON'T DO THIS
offset: 100 // DON'T DO THIS
}
```
## Consistent Patterns Across Codebase
### Why This Pattern?
1. **Consistency**: JWT IDs provide stable, chronological ordering
2. **Performance**: Index-based lookups are faster than OFFSET
3. **Reliability**: Results don't shift when new records are added
## Implementation Checklist
When creating new paginated endpoints:
- Use `beforeId`/`afterId` query parameters in routes
- Pass these parameters to database methods as `afterIdInput`/`beforeIdInput`
- Use JWT ID comparisons (`jwtId > ?`, `jwtId < ?`) in SQL
- Include `DEFAULT_LIMIT` constant in SQL for consistent limits
- Return `{ data: [], hitLimit: boolean }` response format
- Document parameters with standard JSDoc format
- Never use `limit`, `offset`, `page`, or similar parameters
# Database Migration Documentation
## CRITICAL: Always Update sql/README.md
**Every database migration MUST be documented in `sql/README.md`** because migration files cannot be edited after flyway has run.
## Migration Documentation Process
When creating a new migration file (e.g., `V18__new_feature.sqlite3`):
1. **Create the migration file** with proper SQL schema changes
2. **Immediately update `sql/README.md`** with the new schema
3. **Add explanatory comments** for complex fields or relationships
4. **Update indexes** documentation if new indexes are created
## Schema Update Checklist
When creating a migration:
- [ ] Create migration file with proper version number (VXX__description.sqlite3)
- [ ] Add comprehensive SQL comments in migration file
- [ ] Update sql/README.md with complete table schema
- [ ] Document all new indexes in README.md
- [ ] Explain field purposes and constraints
- [ ] Verify README.md reflects current schema state
- [ ] Test migration on development database
## Why This Documentation Matters
1. **Flyway Immutability**: Migration files cannot be edited after deployment
2. **Schema Clarity**: README.md provides the definitive current schema
3. **Developer Onboarding**: New developers need clear schema documentation
4. **Maintenance**: Future changes require understanding current structure
# Data Privacy and DID Hiding
## hideDidsAndAddLinksToNetwork Function
The `hideDidsAndAddLinksToNetwork` function is a critical security component that ensures searches for DIDs (Decentralized Identifiers) only show results that are visible to users who have proper permissions to see them.
### Purpose
This function processes API response data to:
1. **Hide DIDs** that the requesting user doesn't have permission to see
2. **Add network links** showing which DIDs in the user's network can see hidden data
3. **Include public URLs** for DIDs that have published public profiles
4. **Validate search terms** to prevent data leakage through search queries
### Function Signatures
```javascript
// Primary function
async function hideDidsAndAddLinksToNetwork(requesterDid, inputData, searchTerms)
// Wrapper for responses with a "data" key
async function hideDidsAndAddLinksToNetworkInDataKey(requesterDid, input, searchTerms)
```
### Critical searchTerms Parameter
**SECURITY REQUIREMENT**: The `searchTerms` parameter MUST contain all user-provided query data (since it might include DIDs or parts of DIDs).
#### Why searchTerms is Required
The function validates that after hiding DIDs, all search terms are still visible in the result. This prevents users from:
- Searching for a DID they can't see and getting results that reveal its existence
- Using partial DID strings to probe for hidden data
- Discovering private information through search inference
#### Implementation Pattern
```javascript
// ✅ CORRECT - Include all user query parameters that might contain DIDs
const searchTermMayBeDIDs = [
req.query.claimContents,
req.query.issuer,
req.query.subject,
req.query.handleId
]
dbService.someMethod(params)
.then(results => hideDidsAndAddLinksToNetwork(
res.locals.authTokenIssuer,
results,
searchTermMayBeDIDs
))
// ❌ WRONG - Empty array when user provided search terms
.then(results => hideDidsAndAddLinksToNetwork(
res.locals.authTokenIssuer,
results,
[] // DON'T DO THIS if user provided searchable parameters
))
```
### Usage Guidelines
#### When to Use Empty searchTerms Array
Use `[]` only when:
- No user input was used to filter the results
- The endpoint doesn't accept search parameters
- You're certain no DIDs could be in the query parameters
#### When to Include Search Terms
Always include search terms when:
- User provided `claimContents`, `issuer`, `subject`, `handleId` parameters
- Any text search functionality is involved
- User input could contain DIDs or partial DIDs
- The query filters data based on user-provided strings
### Security Checklist
When implementing endpoints, always consider usage of `hideDidsAndAddLinksToNetwork`:
- [ ] Use it for almost every endpoint, and exceptions should be vetted and documented
- [ ] Identify all user-provided query parameters
- [ ] Include any parameter that could contain DIDs in `searchTerms`
- [ ] Include any text search parameters in `searchTerms`
- [ ] Use empty array `[]` only when no user input affects the query
- [ ] Test that search results don't leak hidden DID information
- [ ] Verify that partial DID searches are properly filtered
### Error Prevention
**Red Flags** - These patterns indicate potential security issues:
- Using `[]` for searchTerms when user provided search parameters
- Not including text search fields in searchTerms
- Forgetting to include DID-related query parameters
- Bypassing the function for "simple" endpoints that still return DID data
**Green Flags** - These patterns indicate proper usage:
- Including all user query parameters that might contain DIDs
- Using `[]` only for endpoints with no user search input
- Consistent application across all endpoints returning DID data
- Proper testing of search result filtering