Security
Relevant source files
The following files were used as context for generating this wiki page:
The security architecture of Vinesight is designed to be multi-layered, providing robust protection for user data at the application, API, and database levels. The system leverages a combination of custom middleware for request validation, Supabase's Row Level Security (RLS) for data isolation, and proactive measures against common web vulnerabilities. The core philosophy is to enforce strict access controls and validate all incoming data to maintain data integrity and confidentiality.
The primary components of the security model are the SecurityService class, which acts as a gatekeeper for API requests, and the PostgreSQL database schema, which enforces data access policies directly at the database level. This ensures that even if application-level checks were bypassed, unauthorized data access is prevented.
API Security
API security is centralized within the SecurityService class, which provides a comprehensive set of checks for all incoming API requests.
Sources: src/lib/security-service.ts
Request Validation Flow
The checkAPIRequest static method is the primary entry point for validating API requests. It performs a sequence of checks to ensure the request is legitimate and safe to process.
This diagram illustrates the validation steps for an incoming API request.
Sources: src/lib/security-service.ts:13-100
The validation process includes the following checks:
| Check | Description | Status Code on Failure |
|---|---|---|
| Method Validation | Ensures the request's HTTP method (e.g., GET, POST) is in the list of allowed methods. | 405 Method Not Allowed |
| Rate Limiting | Uses a global rate limiter based on the client's IP address. Authenticated users may have different limits. | 429 Too Many Requests |
| Authentication | Checks for a valid authorization header if the endpoint requires authentication. | 401 Unauthorized |
| Content-Type | For POST, PUT, and PATCH requests, verifies the content-type header is application/json. | 400 Bad Request |
| CSRF Protection | For state-changing methods, validates the x-csrf-token header against a session token. | 403 Forbidden |
| User-Agent | Checks for the presence and minimum length of the User-Agent header to filter out simple bots. | 400 Bad Request |
Sources: src/lib/security-service.ts:13-100
Payload Security
For requests that contain a body, the validatePayload method provides additional security checks before the JSON is parsed.
Sources: src/lib/security-service.ts:105-156
- Size Check: The request body is read as text and its length is compared against a
maxSizelimit (default 10KB) to prevent payload-too-large attacks. If exceeded, it returns a413 Payload Too Largeerror. - Suspicious Content Scan: The raw text is scanned for patterns indicative of common attacks like XSS, SQL Injection, and Template Injection.
- Prototype Pollution Check: After parsing the JSON, the resulting object is recursively scanned for dangerous keys like
__proto__,constructor, orprototypeto prevent prototype pollution.
The following patterns are considered suspicious and will cause a request to be rejected with a 400 Bad Request status:
// src/lib/security-service.ts:164-173
const suspiciousPatterns = [
/\b(eval|setTimeout|setInterval)\s*\(/i,
/<script[\s\S]*?>/i,
/javascript:/i,
/data:text\/html/i,
/(union|select|insert|update|delete|drop)\s+/i,
/(\$\{|\$\(|\{\{)/, // Template injection patterns
/__proto__|constructor\.prototype|prototype\.constructor/i,
]
Sources: src/lib/security-service.ts:164-173
Secure Responses
To ensure security is maintained on the outbound side, the SecurityService provides helper methods to create responses with security-enhancing HTTP headers.
The getSecureHeaders method returns a standard set of headers applied to all secure responses.
| Header | Value | Purpose |
|---|---|---|
X-Content-Type-Options | nosniff | Prevents MIME-type sniffing. |
X-Frame-Options | DENY | Prevents clickjacking by disallowing the page to be rendered in an <iframe>. |
X-XSS-Protection | 1; mode=block | Enables the browser's built-in XSS filter. |
Referrer-Policy | strict-origin-when-cross-origin | Controls how much referrer information is sent with requests. |
Cache-Control | no-store, must-revalidate | Disables caching of sensitive data. |
Pragma | no-cache | Backward compatibility for Cache-Control. |
Sources: src/lib/security-service.ts:192-201
Database Security
Database security is primarily enforced using Supabase's Row Level Security (RLS), a feature of PostgreSQL. This ensures that data access rules are applied directly at the database layer, providing a critical line of defense.
Sources: supabase-schema.sql, CLAUDE.md, AGENTS.md:10
Data Isolation with Row Level Security (RLS)
RLS is enabled on all tables containing user-specific data. Policies are defined to ensure that users can only perform SELECT, INSERT, UPDATE, or DELETE operations on rows that they "own." Ownership is determined by matching the user_id column in a table with the ID of the currently authenticated user, which is available through Supabase's auth.uid() function.
The following diagram shows the relationship between the auth.users table and other data tables, which is the basis for RLS.
Sources: supabase-schema.sql:8-90, CLAUDE.md:21-78
RLS Policy Examples
Policies are defined for each table to restrict access. For tables directly linked to a user (like farms), the policy is straightforward.
Policy for farms table:
This policy allows a user to select only the farms where their id matches the user_id column.
-- supabase-schema.sql:202-203
CREATE POLICY "Users can view their own farms" ON farms FOR SELECT USING (auth.uid() = user_id);
Sources: supabase-schema.sql:202-203
For tables with an indirect relationship (like irrigation_records, which belongs to a farm), the policy uses a subquery (EXISTS) to check for ownership of the parent farm.
Policy for irrigation_records table:
This policy allows a user to select irrigation records only if they own the associated farm.
-- supabase-schema.sql:208-210
CREATE POLICY "Users can view their farm irrigation records" ON irrigation_records FOR SELECT USING (
EXISTS (SELECT 1 FROM farms WHERE farms.id = irrigation_records.farm_id AND farms.user_id = auth.uid())
);
Sources: supabase-schema.sql:208-210
Similar policies for INSERT, UPDATE, and DELETE are applied to all relevant tables, ensuring a consistent and strict data access model across the entire database.
Sources: supabase-schema.sql:199-335, CLAUDE.md:415-455
User-Facing Security Commitments
The application's privacy policy and terms of service outline the security measures promised to users, which are implemented by the technical architecture described above.
Key commitments include:
- Data Encryption: All data transmitted and stored is encrypted.
- Secure Infrastructure: Data is hosted on secure cloud servers.
- Access Control: Strict access controls and authentication protocols are in place, implemented via Supabase RLS and the
SecurityService. - No Data Selling: A firm policy against selling, renting, or trading personal or farming data.
- Prohibited Activities: The terms explicitly prohibit attempts to reverse engineer, compromise system security, or use automated tools without permission.
Sources: src/app/privacy/page.tsx:81-91, 212-219, src/app/terms/page.tsx:206-220