DEV Community

Cover image for CLI to Scan JS/TS Apps for India's DPDP Act Compliance
Ankit Babbar
Ankit Babbar

Posted on

CLI to Scan JS/TS Apps for India's DPDP Act Compliance

Your Next.js App Is Probably Breaking Indian Law

India's Digital Personal Data Protection Act (DPDP), 2023 is now in effect. If your app serves Indian users and does any of the following, you're non-compliant:

  • Loads Google Analytics / Facebook Pixel / Hotjar before the user consents
  • Stores Aadhaar numbers, PAN, phone numbers in localStorage without encryption
  • Sets document.cookie without a consent check
  • Sends PII (email, phone) to analytics platforms
  • Has no privacy policy page
  • Exports user data in bulk without audit logging

Most Indian dev teams don't even know this. The fines go up to ₹250 crore.

Introducing @dpdp-india/audit

A static analysis CLI that scans your JS/TS codebase and flags DPDP violations — like ESLint, but for privacy.

npx @dpdp-india/audit ./src
Enter fullscreen mode Exit fullscreen mode

Output looks like this:

  DPDP Audit Report
  Framework: Next.js | Files scanned: 47

  src/components/Analytics.tsx
    ✗ 12:5  Google Analytics loaded without consent guard  dpdp/no-unguarded-tracker
      → Wrap in a consent check: if (hasConsent('analytics')) { ... }

  src/lib/cache.ts
    ✗ 8:3  PII stored in localStorage without encryption  dpdp/no-raw-pii-storage
      → Use encrypted storage or a server-side session

  src/utils/tracking.ts
    ✗ 23:7  PII field 'email' passed to analytics call  dpdp/no-pii-in-analytics
      → Remove PII fields or hash before sending

  3 errors, 0 warnings
Enter fullscreen mode Exit fullscreen mode

13 Rules, Real Violations

Not theoretical. These catch patterns I've seen in production Indian apps:

Rule What it catches
no-unguarded-tracker GA/GTM/FB/Hotjar/Clarity loaded without consent
no-raw-pii-storage Aadhaar, PAN, email, phone in localStorage/sessionStorage
no-unconsented-cookies document.cookie writes without consent check
no-third-party-scripts Tracker <script> tags in HTML files
no-pii-in-analytics PII fields sent to tracking calls
no-pii-in-headers PII leaked in HTTP headers
no-unguarded-plausible PlausibleProvider without consent wrapper
no-unencrypted-pii-cache PII in cache/Redis without encryption
no-unguarded-database-export DB exports without access control
no-bulk-export-without-audit Bulk data export without audit trail
no-impersonation-without-audit User impersonation without logging
require-data-deletion No hard-delete endpoint (only soft-delete)
require-privacy-policy Missing privacy policy page/route

Next.js? Drop-in Consent SDK

The scanner is one half. The other half is a ready-to-use consent system for Next.js apps:

npm install @dpdp-india/audit
Enter fullscreen mode Exit fullscreen mode

1. Add ConsentProvider + Banner

// app/layout.tsx
import { ConsentProvider, ConsentBanner } from '@dpdp-india/audit/next'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ConsentProvider>
          {children}
          <ConsentBanner
            privacyPolicyUrl="/privacy-policy"
            companyName="Your Company"
          />
        </ConsentProvider>
      </body>
    </html>
  )
}
Enter fullscreen mode Exit fullscreen mode

You get a styled banner with Accept All / Reject / Preferences — handles 4 categories: necessary, analytics, marketing, preferences. State persists in dpdp_consent cookie.

2. Guard Your Scripts

Replace next/script with DpdpScript. Only loads after consent:

import { DpdpScript } from '@dpdp-india/audit/next'

<DpdpScript
  consentCategory="analytics"
  src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"
  strategy="afterInteractive"
/>
Enter fullscreen mode Exit fullscreen mode

3. Use the Hook Anywhere

import { useConsent } from '@dpdp-india/audit/next'

function MyComponent() {
  const { hasConsent, acceptAll, rejectAll } = useConsent()

  if (hasConsent('analytics')) {
    // safe to fire analytics
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Build-time Enforcement

Add to next.config.js — fails the build if violations exist:

import { withDpdpAudit } from '@dpdp-india/audit/next'

export default withDpdpAudit(nextConfig, {
  failOnError: true,
  ignore: ['node_modules/**', '.next/**'],
})
Enter fullscreen mode Exit fullscreen mode

CI/CD Integration

SARIF output plugs into GitHub Code Scanning:

# .github/workflows/dpdp.yml
name: DPDP Compliance
on: [push, pull_request]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx @dpdp-india/audit ./src --format sarif --output dpdp.sarif
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: dpdp.sarif
Enter fullscreen mode Exit fullscreen mode

Violations show up as annotations on your PRs.

How It Works Under the Hood

The scanner uses ts-morph for AST analysis and htmlparser2 for HTML files. Each rule is a function that receives parsed source files and returns violations with:

  • File path, line, column
  • Severity (error/warning)
  • Human-readable message
  • Fix suggestion

No runtime overhead. Static analysis only. Auto-detects your framework (Next.js, React, Angular, Vue, Svelte) and adjusts rules accordingly.

Programmatic API

Use it in your own tools:

import { scan } from '@dpdp-india/audit'

const result = await scan('./src', {
  rules: ['no-unguarded-tracker', 'no-raw-pii-storage'],
  ignore: ['tests/**'],
})

console.log(`${result.violations.length} violations found`)
Enter fullscreen mode Exit fullscreen mode

Why I Built This

Every Indian startup I've audited has at least 3-4 DPDP violations. Most don't know the act exists. The penalty structure is severe — up to ₹250 crore per instance.

There's no ESLint plugin for this. No Semgrep rules. Nothing in the JS/TS ecosystem that understands Indian privacy law.

So I built one.

Get Started

# Scan your project right now
npx @dpdp-india/audit ./src
Enter fullscreen mode Exit fullscreen mode

GitHub: github.com/babbarankit/dpdp-audit
npm: @dpdp-india/audit


Star the repo if this is useful. PRs welcome — especially for new rules and framework integrations.

Top comments (0)