Hasura's declarative RBAC is powerful — but dangerously quiet when misconfigured.
You might think guest
has no write access… until a policy-less table says otherwise.
This guide walks you through building a CLI tool that parses Hasura metadata and verifies RBAC rules statically, before deployment.
1. Why Static Validation?
Hasura permissions live in metadata/
as YAML or JSON.
You can validate them without running the server — ideal for CI pipelines.
Use cases:
- Detect tables with no
select
restrictions - Ensure
public
role can’t write - Enforce field-level allowlists
- Audit policy completeness per role
2. Directory Structure (exported via Hasura CLI)
metadata/
├── tables/
│ ├── user.yaml
│ ├── invoice.yaml
├── actions/
├── sources.yaml
├── version.yaml
Each *.yaml
under tables/
contains permissions by role:
select_permissions:
- role: user
permission:
columns: [id, email]
filter:
id: { _eq: X-Hasura-User-Id }
3. CLI Tool Overview
We'll build a Node.js script that:
- Loads all
tables/*.yaml
- Parses permissions per role
- Applies validation rules
- Outputs audit results or fails with nonzero exit code
4. Install Dependencies
npm init -y
npm install yaml fs glob chalk
5. Example CLI Logic
import fs from 'fs';
import path from 'path';
import yaml from 'yaml';
import glob from 'glob';
import chalk from 'chalk';
const PERMIT_ROLES = ['admin', 'user'];
const metadataPath = './metadata/tables';
const files = glob.sync(`${metadataPath}/*.yaml`);
for (const file of files) {
const content = fs.readFileSync(file, 'utf8');
const doc = yaml.parse(content);
const table = `${doc.table.schema}.${doc.table.name}`;
// Check for public access
const publicInsert = doc.insert_permissions?.find(p => p.role === 'public');
if (publicInsert) {
console.log(chalk.red(`❌ PUBLIC role has insert access on ${table}`));
process.exitCode = 1;
}
// Check for missing filters
for (const role of PERMIT_ROLES) {
const sel = doc.select_permissions?.find(p => p.role === role);
if (sel && (!sel.permission.filter || Object.keys(sel.permission.filter).length === 0)) {
console.log(chalk.yellow(`⚠️ Role ${role} has unfiltered SELECT on ${table}`));
}
}
}
6. Run It in CI
"scripts": {
"validate:rbac": "node rbac-check.js"
}
Then add to .github/workflows/ci.yml
or GitLab CI pipeline:
- name: Validate Hasura RBAC
run: npm run validate:rbac
7. Extending the Ruleset
- 🔒 Block full-field exposure (
columns: '*'
) - 🧪 Verify insert/update/delete have field filters
- 📊 Export role × table permission matrix
- 🧬 Compare across environments (dev vs prod metadata)
Final Thoughts
Hasura metadata is auditable. You just need to look.
By statically verifying your RBAC before deployment,
you eliminate entire classes of silent misconfiguration bugs — before they hit production.
Next:
- Generate HTML or CSV access matrix
- Integrate with Slack alerting
- Build a VSCode plugin for live feedback on permission diffs
Fail fast. Trust static. Secure declaratively.