{"id":51175604,"url":"https://github.com/nur-zaman/sql-guard","last_synced_at":"2026-06-27T03:31:15.490Z","repository":{"id":341563914,"uuid":"1170611419","full_name":"nur-zaman/sql-guard","owner":"nur-zaman","description":"Validate AI generated PostgreSQL queries against explicit allowlists. This package parses SQL into an AST and denies anything outside your policy.","archived":false,"fork":false,"pushed_at":"2026-04-04T20:39:11.000Z","size":76,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T22:41:23.749Z","etag":null,"topics":["security","sql","sql-injection","sql-sanitizer"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/sql-guard","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nur-zaman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-02T10:13:48.000Z","updated_at":"2026-04-04T20:36:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nur-zaman/sql-guard","commit_stats":null,"previous_names":["nur-zaman/sql-guard"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nur-zaman/sql-guard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nur-zaman%2Fsql-guard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nur-zaman%2Fsql-guard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nur-zaman%2Fsql-guard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nur-zaman%2Fsql-guard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nur-zaman","download_url":"https://codeload.github.com/nur-zaman/sql-guard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nur-zaman%2Fsql-guard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34840899,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["security","sql","sql-injection","sql-sanitizer"],"created_at":"2026-06-27T03:31:14.467Z","updated_at":"2026-06-27T03:31:15.481Z","avatar_url":"https://github.com/nur-zaman.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sql-guard\n\nValidate AI generated PostgreSQL queries against explicit allowlists. This package parses SQL into an AST and denies anything outside your policy.\n\n## Installation\n\n```bash\nnpm install sql-guard\n```\n\n## Quickstart\n\n```typescript\nimport { validate, assertSafeSql, ErrorCode } from 'sql-guard';\n\nconst policy = {\n  allowedTables: ['public.users', 'public.orders'],\n  allowedFunctions: ['count', 'lower'],\n};\n\nconst result = validate('SELECT * FROM public.users', policy);\nif (!result.ok) {\n  console.log('Denied:', result.errorCode);\n  console.log('Violations:', result.violations);\n}\n\n// Or fail fast with an exception\nassertSafeSql('SELECT lower(u.email) FROM public.users u', policy);\n```\n\n## API Reference\n\n### validate(sql, policy)\n\nValidates SQL against a policy.\n\n- Returns: `ValidationResult`\n- On failure: `ok === false`, `violations` populated, and `errorCode` set\n\n### assertSafeSql(sql, policy)\n\nValidates SQL and throws when validation fails.\n\n- Returns: `void`\n- Throws: `SqlValidationError` with `code: ErrorCode` and `violations: Violation[]`\n\n```typescript\nimport { assertSafeSql, SqlValidationError, ErrorCode } from 'sql-guard';\n\ntry {\n  assertSafeSql('SELECT pg_catalog.current_database() FROM public.users', {\n    allowedTables: ['public.users'],\n    allowedFunctions: ['lower'],\n  });\n} catch (err) {\n  if (err instanceof SqlValidationError) {\n    if (err.code === ErrorCode.FUNCTION_NOT_ALLOWED) {\n      console.error('Blocked a function call:', err.violations);\n    }\n  }\n  throw err;\n}\n```\n\n### ErrorCode\n\nEnum of error codes returned by `validate()` and used by `SqlValidationError`.\n\n### Policy\n\nPolicy settings that drive validation.\n\n```ts\nexport interface Policy {\n  allowedTables: string[];\n  allowedStatements?: ('select' | 'insert' | 'update' | 'delete')[];\n  allowMultiStatement?: boolean;\n  allowedFunctions?: string[];\n  tableIdentifierMatching?: 'strict' | 'caseInsensitive';\n  resolver?: (unqualified: string) =\u003e string | null;\n  defaultSchema?: string;\n}\n```\n\nDefaults and behavior:\n\n- `allowedTables` is required.\n- `allowedTables` entries must be schema-qualified (`schema.table`). Invalid entries return `INVALID_POLICY`.\n- `allowedStatements` defaults to `['select']`.\n- `allowMultiStatement` defaults to `false`.\n- `allowedFunctions` defaults to `[]`, which means any function call is denied unless allowlisted.\n- `tableIdentifierMatching` defaults to `'strict'` (exact case-sensitive table matching).\n- Set `tableIdentifierMatching: 'caseInsensitive'` to preserve case-insensitive table matching.\n- Unqualified table references in SQL are denied unless you provide `defaultSchema` or `resolver` to map them to `schema.table`.\n- `defaultSchema`: when provided, unqualified `allowedTables` entries are auto-qualified with this schema, and unqualified SQL references resolve to it.\n- `resolver`: optional function to map unqualified names to qualified names. Takes precedence over `defaultSchema`.\n- Metadata schemas (`information_schema`, `pg_catalog`) are treated specially and must be explicitly allowlisted even when using `defaultSchema`. Setting `defaultSchema` to a metadata schema name does not grant automatic access.\n- Unqualified function allowlist entries (for example, `lower`) match only unqualified calls (`lower(...)`).\n- Schema-qualified function calls require schema-qualified allowlist entries (`pg_catalog.current_database`).\n\nPolicy examples:\n\n```ts\n// Explicit schema-qualified tables\nconst strictPolicy = {\n  allowedTables: ['public.users', 'analytics.events'],\n  allowedFunctions: ['lower', 'pg_catalog.current_database'],\n  resolver: (unqualified: string) =\u003e\n    unqualified === 'users' ? 'public.users' : null,\n};\n\n// Using defaultSchema for simpler configuration\nconst defaultSchemaPolicy = {\n  defaultSchema: 'public',\n  allowedTables: ['users', 'orders', 'products'],\n  // Treated as ['public.users', 'public.orders', 'public.products']\n};\n\n// Mixed: defaultSchema + explicit qualified tables\nconst mixedPolicy = {\n  defaultSchema: 'public',\n  allowedTables: ['users', 'analytics.events'],\n  // Treated as ['public.users', 'analytics.events']\n};\n\n// Resolver takes precedence over defaultSchema\nconst resolverPolicy = {\n  defaultSchema: 'public',\n  allowedTables: ['public.users', 'archive.users'],\n  resolver: (name: string) =\u003e\n    name === 'old_users' ? 'archive.users' : null,\n  // 'users' resolves to 'public.users' via defaultSchema\n  // 'old_users' resolves to 'archive.users' via resolver\n};\n```\n\n## Security Model\n\n- AST based validation, not regex matching.\n- Fail closed: unsupported or uncertain parser features are denied.\n- Data-modifying CTE payloads (for example `WITH x AS (INSERT ...) SELECT ...`) are denied as unsupported.\n- `SELECT INTO` is denied as unsupported.\n- Table allowlists: every referenced table must be in `policy.allowedTables` by fully qualified name.\n- Statement type restrictions: only `select` is allowed unless you opt in via `allowedStatements`.\n- Multi statement restriction: `SELECT 1; SELECT 2` is denied unless `allowMultiStatement: true`.\n- Function allowlists: schema-qualified calls are allowed only by exact schema-qualified entries.\n- Metadata table protection: relations in `information_schema` and `pg_catalog` are denied unless explicitly allowlisted by fully qualified name.\n\nThis is a guardrail for LLM output. It helps enforce least privilege at the query shape level. Use it alongside parameterization, prepared statements, and database permissions.\n\n## Limitations\n\n- PostgreSQL focused (v1). Other dialects are not supported.\n- No SQL rewriting or sanitization. This package validates, it doesn't transform queries.\n- Not a complete SQL injection defense by itself. Treat it as defense in depth.\n- No database context: it can't check column level permissions, RLS policies, or runtime schema changes.\n\n## Error Codes\n\n`validate()` returns a single `errorCode` plus a list of `violations`. Invalid policy configuration is reported before SQL parsing.\n\n| Code | Description |\n|------|-------------|\n| `PARSE_ERROR` | SQL could not be parsed into an AST. |\n| `UNSUPPORTED_SQL_FEATURE` | Parsed SQL contains features outside the supported subset (fail closed). |\n| `TABLE_NOT_ALLOWED` | A referenced table is not in `policy.allowedTables`, or an unqualified table can't be resolved. |\n| `STATEMENT_NOT_ALLOWED` | Statement type is not allowed (defaults to `select` only). |\n| `FUNCTION_NOT_ALLOWED` | A function call is not in `policy.allowedFunctions`. |\n| `MULTI_STATEMENT_DISABLED` | Query contains multiple statements while `allowMultiStatement` is disabled. |\n| `INVALID_POLICY` | Policy configuration is invalid (for example non-qualified table allowlist entries). |\n\n## Violation Types\n\n`Violation.type` can be:\n\n- `parse`\n- `unsupported`\n- `policy`\n- `statement`\n- `table`\n- `function`\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnur-zaman%2Fsql-guard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnur-zaman%2Fsql-guard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnur-zaman%2Fsql-guard/lists"}