home / skills / trantuananh-17 / product-reviews / security

security skill

/.claude/skills/security

This skill helps you audit security risk, enforce authentication, and prevent IDOR by applying proven patterns for auth, PII protection, and webhooks.

npx playbooks add skill trantuananh-17/product-reviews --skill security

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
5.8 KB
---
name: security-audit
description: Use this skill when the user asks to "audit security", "check for vulnerabilities", "review authentication", "prevent IDOR", "protect customer data", "verify webhooks", "check HMAC", or any security-related review work. Provides security patterns for authentication, authorization, IDOR prevention, PII protection, and webhook verification.
---

# Security Patterns (packages/functions)

> For **API design patterns**, see `api-design` skill

## Critical Vulnerabilities

| Vulnerability | Risk | Example |
|--------------|------|---------|
| **IDOR** | High | User A accesses User B's data via `/api/customer/123` |
| **Unauthenticated PII** | Critical | Returning email in public API response |
| **Missing Auth** | Critical | `/popup/*` endpoints without authentication |
| **Shop Isolation** | Critical | Shop A accessing Shop B's data |

---

## Authentication

### Endpoint Types

| Endpoint Type | Auth Required | Example |
|--------------|---------------|---------|
| Admin API | Shop session + JWT | `/api/admin/*` |
| Storefront API | Customer token OR signature | `/api/storefront/*` |
| Popup/Widget | HMAC signature | `/popup/*` |
| Webhook | Shopify HMAC | `/webhooks/*` |
| Public | None (no sensitive data) | `/health`, `/status` |

### Admin Controller

```javascript
import {getCurrentShop} from '@functions/helpers/auth';

async function getCustomers(ctx) {
  const shopId = getCurrentShop(ctx);  // From authenticated session
  const customers = await customerRepo.getByShopId(shopId);
  ctx.body = {success: true, data: customers};
}
```

---

## IDOR Prevention

### Audit Checklist

| Check | Secure | Vulnerable |
|-------|--------|------------|
| Shop ID source | `getCurrentShop(ctx)`, `ctx.state.shop.id` | `ctx.params`, `ctx.query` |
| Query scoping | `.where('shopId', '==', shopId)` | `.doc(id).get()` alone |
| Ownership check | `if (resource.shopId !== shopId)` | Return data directly |
| Update/Delete | Verify ownership first | `repo.update(id, data)` |

### Grep Commands for Audit

```bash
grep -rn "ctx.params.shopId" controllers/
grep -rn "ctx.params.customerId" controllers/
grep -rn "getById(" repositories/
grep -rn ".doc(.*).get()" repositories/
```

### Secure Pattern

```javascript
async function getOrder(ctx) {
  const shopId = getCurrentShop(ctx);
  const {orderId} = ctx.params;

  const order = await orderRepo.getById(orderId);

  if (order.shopId !== shopId) {
    ctx.status = 403;
    return;
  }

  ctx.body = {success: true, data: order};
}
```

---

## PII Protection

### Classification

| Data Type | Classification | Public Endpoint? |
|-----------|---------------|------------------|
| Email, Phone, Address | PII | Never |
| Date of Birth | PII | Never |
| Payment Info | Sensitive PII | Never |
| First Name | Low Risk | With signature |
| Points, Tier | Non-PII | With signature |

### Secure Response

```javascript
// Only return non-sensitive data
ctx.body = {
  firstName: customer.firstName,
  points: customer.points,
  tier: customer.tier
  // Never: email, phone, address
};
```

---

## HMAC Verification

### Popup Signature

```javascript
import crypto from 'crypto';

function verifyPopupSignature(ctx) {
  const {shopId, customerId, timestamp, signature} = ctx.query;

  // Reject old requests
  if (Date.now() - parseInt(timestamp) > 5 * 60 * 1000) return false;

  const expected = crypto
    .createHmac('sha256', process.env.POPUP_SECRET)
    .update(`${shopId}:${customerId}:${timestamp}`)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
```

### Shopify Webhook

```javascript
function verifyShopifyWebhook(ctx) {
  const hmac = ctx.get('X-Shopify-Hmac-Sha256');
  const calculated = crypto
    .createHmac('sha256', process.env.SHOPIFY_API_SECRET)
    .update(ctx.request.rawBody, 'utf8')
    .digest('base64');

  return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(calculated));
}
```

### Webhook Vulnerabilities

| Pattern | Risk |
|---------|------|
| HMAC bypass headers | CRITICAL |
| No HMAC verification | HIGH |
| Missing timestamp validation | MEDIUM |

---

## Input Validation

```javascript
async function updateCustomer(ctx) {
  const shopId = getCurrentShop(ctx);
  const {customerId} = ctx.params;
  const {firstName, lastName} = ctx.request.body;  // Whitelist fields

  const customer = await customerRepo.getById(customerId);
  if (customer.shopId !== shopId) {
    ctx.status = 403;
    return;
  }

  await customerRepo.update(customerId, {
    firstName: firstName?.trim().slice(0, 50),
    lastName: lastName?.trim().slice(0, 50)
  });
}
```

---

## Database Security

### Firestore Rules

```
match /customers/{customerId} {
  allow read, write: if request.auth != null
    && request.auth.token.shopId == resource.data.shopId;
}
```

### Storage Rules

```
match /shops/{shopId}/{allPaths=**} {
  allow read, write: if request.auth != null
    && request.auth.token.shopId == shopId
    && request.resource.size < 5 * 1024 * 1024;
}
```

---

## Best Practices

| Do | Don't |
|----|-------|
| Get shopId from `getCurrentShop(ctx)` | Use `ctx.params.shopId` |
| Scope all queries by shopId | Query without shop filter |
| Whitelist response fields | Return full objects |
| Verify HMAC on webhooks | Trust headers blindly |
| Validate and sanitize inputs | Use `ctx.request.body` directly |
| Use `crypto.timingSafeEqual` | Compare strings with `===` |

---

## Security Checklist

```
Authentication:
□ Sensitive endpoints require auth
□ Shop ID from session, not params
□ Customer ID verified against token

Authorization:
□ Users access only their data
□ Shop isolation verified
□ No IDOR vulnerabilities

Data Protection:
□ No PII in unauthenticated responses
□ Response fields whitelisted
□ Inputs validated and sanitized

Webhooks:
□ HMAC verification on all webhooks
□ Timestamp validation
□ No bypass headers
```

Overview

This skill provides a focused security audit checklist and concrete patterns for web APIs and services. It highlights high-risk issues like IDOR, unauthenticated PII exposure, missing auth, shop isolation, webhook HMAC verification, and input validation. Use it to quickly assess authentication, authorization, PII handling, webhook integrity, and database rules.

How this skill works

It inspects endpoint types, auth requirements, and common code patterns to identify insecure sources of shop or user identity (params/query vs. session). It verifies HMAC webhook and popup signature logic, checks ownership enforcement for reads/updates/deletes, and ensures PII is never returned from public endpoints. The skill provides grep commands, secure code snippets, Firestore/storage rule examples, and a checklist for manual audits.

When to use it

  • When asked to audit security or check for vulnerabilities in APIs
  • When reviewing authentication and authorization flows
  • To prevent IDOR or shop-data leakage
  • When validating webhook or popup signature implementations
  • When ensuring PII is excluded from public responses
  • During pre-release security reviews or incident triage

Best practices

  • Always derive shopId from authenticated session/state (getCurrentShop) not request params
  • Scope all queries by shopId and verify resource.shopId before read/update/delete
  • Whitelist response fields; never return email/phone/address on public endpoints
  • Verify HMAC signatures for popups and webhooks and validate timestamps to prevent replay
  • Use crypto.timingSafeEqual for HMAC comparisons and enforce secrets via env variables
  • Validate and sanitize inputs; trim and limit string lengths and whitelist permitted fields

Example use cases

  • Audit controllers and repositories for IDOR using grep commands provided
  • Review popup/widget endpoints to implement HMAC + timestamp validation
  • Validate webhook endpoints against Shopify HMAC best practices and rawBody signing
  • Review API responses to remove PII from unauthenticated or public endpoints
  • Implement Firestore/storage rules that enforce request.auth.token.shopId == resource.shopId
  • Add ownership checks on update/delete flows to prevent cross-shop modification

FAQ

How do I detect IDOR quickly in a codebase?

Search for direct getById/.doc(id).get() calls and usages of ctx.params or ctx.query for shop/user IDs. Replace with patterns that use getCurrentShop(ctx) and ownership checks before returning data.

What if I can’t change an upstream webhook to include an HMAC?

If HMAC is unavailable, require a shared secret in a separate header, restrict IPs, add timestamp+nonce checks, and treat the endpoint as high risk until a proper signature is added.