ts-prorm-orm 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Database Firewall / Query Allowlist
|
|
4
|
+
*
|
|
5
|
+
* A security layer that:
|
|
6
|
+
* - Blocks dangerous queries or commands
|
|
7
|
+
* - Allows only approved query patterns
|
|
8
|
+
* - Logs and alerts on blocked queries
|
|
9
|
+
* - Required for: OWASP, CIS Benchmarks compliance
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* // Create firewall instance
|
|
13
|
+
* const firewall = QueryFirewall.create(prorm, {
|
|
14
|
+
* mode: 'allowlist',
|
|
15
|
+
* onBlock: (block) => console.log('Blocked:', block.query),
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // Add allowed patterns
|
|
19
|
+
* firewall.allow('^SELECT \\* FROM users WHERE');
|
|
20
|
+
* firewall.allow('^INSERT INTO orders');
|
|
21
|
+
*
|
|
22
|
+
* // Add blocked patterns (dangerous commands)
|
|
23
|
+
* firewall.block('^DROP (TABLE|DATABASE)');
|
|
24
|
+
* firewall.block('^TRUNCATE');
|
|
25
|
+
*
|
|
26
|
+
* // Check if query is allowed
|
|
27
|
+
* const result = firewall.checkQuery('SELECT * FROM users WHERE id = 1');
|
|
28
|
+
* if (!result.allowed) {
|
|
29
|
+
* console.log('Query blocked:', result.reason);
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // Get blocked history
|
|
33
|
+
* const history = firewall.getBlockedHistory();
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.QueryFirewall = exports.DATABASE_RULES = exports.SQLITE_RULES = exports.ORACLE_RULES = exports.MSSQL_RULES = exports.MARIADB_RULES = exports.MYSQL_RULES = exports.POSTGRESQL_RULES = exports.NIST_CSF_RULES = exports.SOX_RULES = exports.HIPAA_RULES = exports.PCI_DSS_RULES = exports.CIS_BENCHMARK_RULES = exports.OWASP_SQL_INJECTION_PATTERNS = exports.FIREWALL_RULES = exports.FirewallModeEnum = void 0;
|
|
37
|
+
exports.analyzeQuerySecurity = analyzeQuerySecurity;
|
|
38
|
+
exports.getRecommendedRules = getRecommendedRules;
|
|
39
|
+
exports.FirewallRules = FirewallRules;
|
|
40
|
+
exports.PostgreSQLFirewall = PostgreSQLFirewall;
|
|
41
|
+
exports.MySQLFirewall = MySQLFirewall;
|
|
42
|
+
exports.MariaDBFirewall = MariaDBFirewall;
|
|
43
|
+
exports.MSSQLFirewall = MSSQLFirewall;
|
|
44
|
+
exports.OracleFirewall = OracleFirewall;
|
|
45
|
+
exports.SQLiteFirewall = SQLiteFirewall;
|
|
46
|
+
exports.applyFirewallRulesToModel = applyFirewallRulesToModel;
|
|
47
|
+
/**
|
|
48
|
+
* Companion enum for {@link FirewallMode}. Provided for callers who prefer
|
|
49
|
+
* enum-based ergonomics (autocomplete, refactor-safety) over raw string
|
|
50
|
+
* literals. The underlying values are identical to the `FirewallMode`
|
|
51
|
+
* string literals, so either form may be used interchangeably.
|
|
52
|
+
*/
|
|
53
|
+
var FirewallModeEnum;
|
|
54
|
+
(function (FirewallModeEnum) {
|
|
55
|
+
FirewallModeEnum["Allowlist"] = "allowlist";
|
|
56
|
+
FirewallModeEnum["Blocklist"] = "blocklist";
|
|
57
|
+
FirewallModeEnum["Strict"] = "strict";
|
|
58
|
+
})(FirewallModeEnum || (exports.FirewallModeEnum = FirewallModeEnum = {}));
|
|
59
|
+
// ==================== Firewall Rule Categories ====================
|
|
60
|
+
/** Exportable rule constants for custom configurations */
|
|
61
|
+
exports.FIREWALL_RULES = {
|
|
62
|
+
// Destructive Operations
|
|
63
|
+
DESTRUCTIVE: [
|
|
64
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE|SCHEMA|INDEX|PROCEDURE|FUNCTION|VIEW|MATERIALIZED)/i, description: 'DROP object' },
|
|
65
|
+
{ pattern: /^TRUNCATE\s+(TABLE)?/i, description: 'TRUNCATE table' },
|
|
66
|
+
{ pattern: /^DELETE\s+FROM\s+\w+\s*$/i, description: 'DELETE without WHERE clause' },
|
|
67
|
+
{ pattern: /^ALTER\s+TABLE\s+\w+\s+DROP/i, description: 'ALTER TABLE DROP' },
|
|
68
|
+
{ pattern: /^ALTER\s+TABLE\s+\w+\s+RENAME/i, description: 'ALTER TABLE RENAME' },
|
|
69
|
+
{ pattern: /^DROP\s+INDEX/i, description: 'DROP index' },
|
|
70
|
+
{ pattern: /^DROP\s+TRIGGER/i, description: 'DROP trigger' },
|
|
71
|
+
{ pattern: /^DROP\s+SEQUENCE/i, description: 'DROP sequence' },
|
|
72
|
+
],
|
|
73
|
+
// Schema Modification
|
|
74
|
+
SCHEMA_MODIFICATION: [
|
|
75
|
+
{ pattern: /^CREATE\s+(TABLE|DATABASE|SCHEMA)/i, description: 'CREATE table/database/schema' },
|
|
76
|
+
{ pattern: /^ALTER\s+TABLE\s+\w+\s+ADD\s+COLUMN/i, description: 'ADD column' },
|
|
77
|
+
{ pattern: /^ALTER\s+TABLE\s+\w+\s+MODIFY/i, description: 'MODIFY column' },
|
|
78
|
+
{ pattern: /^ALTER\s+TABLE\s+\w+\s+CHANGE/i, description: 'CHANGE column' },
|
|
79
|
+
{ pattern: /^ALTER\s+TABLE\s+\w+\s+ADD\s+INDEX/i, description: 'ADD index' },
|
|
80
|
+
{ pattern: /^CREATE\s+INDEX/i, description: 'CREATE index' },
|
|
81
|
+
{ pattern: /^CREATE\s+(UNIQUE\s+)?INDEX/i, description: 'CREATE index' },
|
|
82
|
+
],
|
|
83
|
+
// Permission Changes
|
|
84
|
+
PERMISSIONS: [
|
|
85
|
+
{ pattern: /^GRANT\s+/i, description: 'GRANT permissions' },
|
|
86
|
+
{ pattern: /^REVOKE\s+/i, description: 'REVOKE permissions' },
|
|
87
|
+
{ pattern: /^CREATE\s+USER/i, description: 'CREATE user' },
|
|
88
|
+
{ pattern: /^DROP\s+USER/i, description: 'DROP user' },
|
|
89
|
+
{ pattern: /^ALTER\s+USER/i, description: 'ALTER user' },
|
|
90
|
+
{ pattern: /^SET\s+PASSWORD/i, description: 'SET password' },
|
|
91
|
+
],
|
|
92
|
+
// Execution / Code Execution
|
|
93
|
+
CODE_EXECUTION: [
|
|
94
|
+
{ pattern: /^EXEC\s*\(/i, description: 'EXEC stored procedure' },
|
|
95
|
+
{ pattern: /^EXECUTE\s*\(/i, description: 'EXECUTE stored procedure' },
|
|
96
|
+
{ pattern: /^CALL\s+\w+\s*\(/i, description: 'CALL procedure/function' },
|
|
97
|
+
{ pattern: /^xp_cmdshell/i, description: 'xp_cmdshell (SQL Server)' },
|
|
98
|
+
{ pattern: /^sp_executesql/i, description: 'sp_executesql (SQL Server)' },
|
|
99
|
+
{ pattern: /^CREATE\s+(FUNCTION|PROCEDURE)/i, description: 'CREATE function/procedure' },
|
|
100
|
+
{ pattern: /^DROP\s+(FUNCTION|PROCEDURE)/i, description: 'DROP function/procedure' },
|
|
101
|
+
],
|
|
102
|
+
// File Operations
|
|
103
|
+
FILE_OPERATIONS: [
|
|
104
|
+
{ pattern: /^load_file\s*\(/i, description: 'LOAD_FILE() - read file' },
|
|
105
|
+
{ pattern: /^into\s+outfile/i, description: 'INTO OUTFILE - write file' },
|
|
106
|
+
{ pattern: /^into\s+dumpfile/i, description: 'INTO DUMPFILE - write binary file' },
|
|
107
|
+
{ pattern: /^SELECT\s+.+\s+FROM\s+\w+\s+WHERE\s+.+\s+like\s+['"]%[\/\\]/i, description: 'Path traversal attempt' },
|
|
108
|
+
{ pattern: /\/etc\/passwd/i, description: 'Access /etc/passwd' },
|
|
109
|
+
{ pattern: /windows[\/\\]system32/i, description: 'Access Windows system32' },
|
|
110
|
+
],
|
|
111
|
+
// Information Gathering
|
|
112
|
+
INFORMATION_GATHERING: [
|
|
113
|
+
{ pattern: /^SHOW\s+(DATABASES|TABLES|COLUMNS|INDEXES)/i, description: 'SHOW databases/tables/columns' },
|
|
114
|
+
{ pattern: /^DESCRIBE\s+\w+/i, description: 'DESCRIBE table' },
|
|
115
|
+
{ pattern: /^EXPLAIN\s+/i, description: 'EXPLAIN query' },
|
|
116
|
+
{ pattern: /^SELECT\s+@@version/i, description: 'Get database version' },
|
|
117
|
+
{ pattern: /^SELECT\s+user\s*\(\s*\)/i, description: 'Get current user' },
|
|
118
|
+
{ pattern: /^SELECT\s+current_user/i, description: 'Get current user' },
|
|
119
|
+
{ pattern: /^SELECT\s+@@datadir/i, description: 'Get data directory' },
|
|
120
|
+
{ pattern: /^SELECT\s+information_schema/i, description: 'Access information_schema' },
|
|
121
|
+
],
|
|
122
|
+
// Transaction Control
|
|
123
|
+
TRANSACTION_CONTROL: [
|
|
124
|
+
{ pattern: /^BEGIN\s*;?\s*$/i, description: 'BEGIN transaction' },
|
|
125
|
+
{ pattern: /^START\s+TRANSACTION/i, description: 'START TRANSACTION' },
|
|
126
|
+
{ pattern: /^COMMIT\s*;?\s*$/i, description: 'COMMIT transaction' },
|
|
127
|
+
{ pattern: /^ROLLBACK\s*;?\s*$/i, description: 'ROLLBACK transaction' },
|
|
128
|
+
{ pattern: /^SET\s+TRANSACTION\s+ISOLATION/i, description: 'SET TRANSACTION ISOLATION' },
|
|
129
|
+
],
|
|
130
|
+
// Bulk Operations
|
|
131
|
+
BULK_OPERATIONS: [
|
|
132
|
+
{ pattern: /^BULK\s+INSERT/i, description: 'BULK INSERT' },
|
|
133
|
+
{ pattern: /^INSERT\s+INTO\s+\w+\s+SELECT\s+.*\s+FROM\s+\w+\s+WHERE\s+['"]%.*['"]/i, description: 'Bulk insert with wildcard' },
|
|
134
|
+
],
|
|
135
|
+
// Database Links / Remote Operations
|
|
136
|
+
REMOTE_OPERATIONS: [
|
|
137
|
+
{ pattern: /^SELECT\s+.*\s+FROM\s+\w+\.\w+/i, description: 'Cross-database query' },
|
|
138
|
+
{ pattern: /^EXEC\s+at\s+\w+/i, description: 'Remote execution (Oracle)' },
|
|
139
|
+
{ pattern: /^openquery\s*\(/i, description: 'OPENQUERY - linked server' },
|
|
140
|
+
{ pattern: /^opendatasource\s*\(/i, description: 'OPENDATASOURCE' },
|
|
141
|
+
],
|
|
142
|
+
// Encryption/Decryption
|
|
143
|
+
CRYPTO_OPERATIONS: [
|
|
144
|
+
{ pattern: /^CREATE\s+ASYMMETRIC\s+KEY/i, description: 'CREATE asymmetric key' },
|
|
145
|
+
{ pattern: /^CREATE\s+SYMMETRIC\s+KEY/i, description: 'CREATE symmetric key' },
|
|
146
|
+
{ pattern: /^CREATE\s+CERTIFICATE/i, description: 'CREATE certificate' },
|
|
147
|
+
],
|
|
148
|
+
// Backup Operations
|
|
149
|
+
BACKUP_OPERATIONS: [
|
|
150
|
+
{ pattern: /^BACKUP\s+(DATABASE|LOG)/i, description: 'BACKUP database/log' },
|
|
151
|
+
{ pattern: /^RESTORE\s+(DATABASE|LOG)/i, description: 'RESTORE database/log' },
|
|
152
|
+
{ pattern: /^mysqldump/i, description: 'mysqldump command' },
|
|
153
|
+
{ pattern: /^pg_dump/i, description: 'pg_dump command' },
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
/** OWASP Top 10 SQL Injection patterns */
|
|
157
|
+
exports.OWASP_SQL_INJECTION_PATTERNS = [
|
|
158
|
+
/('\s*or\s*'1'\s*=\s*'1)/i,
|
|
159
|
+
/('\s*or\s*1\s*=\s*1)/i,
|
|
160
|
+
/(union\s+select)/i,
|
|
161
|
+
/(union\s+all\s+select)/i,
|
|
162
|
+
/(\bor\b.*\b=\b)/,
|
|
163
|
+
/(exec\s*\()/i,
|
|
164
|
+
/(;\s*drop\s+table)/i,
|
|
165
|
+
/(\/\*.*\*\/)/,
|
|
166
|
+
/(--\s*)/,
|
|
167
|
+
/(char\s*\(\s*\d+)/,
|
|
168
|
+
/(0x[\da-f]+)/i,
|
|
169
|
+
/(\bor\b\s+\d+\s*=\s*\d+)/,
|
|
170
|
+
/('\s+and\s+'\s*=\s*')/i,
|
|
171
|
+
/(having\s+\d+\s*=\s*\d+)/i,
|
|
172
|
+
/(order\s+by\s+\d+)/i,
|
|
173
|
+
/(benchmark\s*\()/i,
|
|
174
|
+
/(sleep\s*\()/i,
|
|
175
|
+
];
|
|
176
|
+
/** CIS Benchmarks database security rules */
|
|
177
|
+
exports.CIS_BENCHMARK_RULES = [
|
|
178
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE|SCHEMA)/i, description: 'CIS: Block DROP TABLE/DATABASE' },
|
|
179
|
+
{ pattern: /^DELETE\s+FROM\s+\w+\s*$/i, description: 'CIS: Block DELETE without WHERE' },
|
|
180
|
+
{ pattern: /^GRANT\s+ALL/i, description: 'CIS: Block GRANT ALL' },
|
|
181
|
+
{ pattern: /^CREATE\s+(FUNCTION|PROCEDURE)\s+.*\s+RETURNS\s+void/i, description: 'CIS: Block void-returning functions' },
|
|
182
|
+
];
|
|
183
|
+
/** PCI DSS compliance rules */
|
|
184
|
+
exports.PCI_DSS_RULES = [
|
|
185
|
+
{ pattern: /^SELECT\s+.*\s+FROM\s+credit_cards/i, description: 'PCI: Direct SELECT on credit_cards table' },
|
|
186
|
+
{ pattern: /^SELECT\s+.*\s+FROM\s+payments\s+WHERE\s+card_number/i, description: 'PCI: Query card numbers' },
|
|
187
|
+
{ pattern: /^INSERT\s+INTO\s+audit_log/i, description: 'PCI: All queries must be logged' },
|
|
188
|
+
{ pattern: /^DELETE\s+FROM\s+audit_log/i, description: 'PCI: Cannot delete audit logs' },
|
|
189
|
+
];
|
|
190
|
+
/** HIPAA compliance rules */
|
|
191
|
+
exports.HIPAA_RULES = [
|
|
192
|
+
{ pattern: /^SELECT\s+.*\s+FROM\s+patients\s+WHERE\s+ssn/i, description: 'HIPAA: Direct SSN query' },
|
|
193
|
+
{ pattern: /^SELECT\s+.*\s+FROM\s+medical_records\s+WHERE\s+diagnosis/i, description: 'HIPAA: Direct diagnosis query' },
|
|
194
|
+
{ pattern: /^SELECT\s+.*\s+FROM\s+phi\s+WHERE/i, description: 'HIPAA: PHI access without audit' },
|
|
195
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE)\s+\w*patient/i, description: 'HIPAA: Cannot drop patient tables' },
|
|
196
|
+
];
|
|
197
|
+
/** SOX compliance rules */
|
|
198
|
+
exports.SOX_RULES = [
|
|
199
|
+
{ pattern: /^DELETE\s+FROM\s+.*\s+WHERE\s+.*\s+<\s+NOW/i, description: 'SOX: Cannot delete old records' },
|
|
200
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE)\s+\w*financial/i, description: 'SOX: Cannot drop financial tables' },
|
|
201
|
+
{ pattern: /^TRUNCATE\s+.*\s+financial/i, description: 'SOX: Cannot truncate financial tables' },
|
|
202
|
+
];
|
|
203
|
+
/** NIST CSF rules */
|
|
204
|
+
exports.NIST_CSF_RULES = [
|
|
205
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE)/i, description: 'NIST: Block DROP operations' },
|
|
206
|
+
{ pattern: /^DELETE\s+FROM\s+\w+\s*$/i, description: 'NIST: Block bulk DELETE' },
|
|
207
|
+
{ pattern: /^GRANT\s+/i, description: 'NIST: Require approval for GRANT' },
|
|
208
|
+
{ pattern: /^REVOKE\s+/i, description: 'NIST: Require approval for REVOKE' },
|
|
209
|
+
];
|
|
210
|
+
/** Default dangerous patterns for general use */
|
|
211
|
+
const DEFAULT_DANGEROUS_PATTERNS = [
|
|
212
|
+
...exports.FIREWALL_RULES.DESTRUCTIVE,
|
|
213
|
+
...exports.FIREWALL_RULES.CODE_EXECUTION,
|
|
214
|
+
...exports.FIREWALL_RULES.FILE_OPERATIONS,
|
|
215
|
+
{ pattern: /^xp_cmdshell/i, description: 'xp_cmdshell (SQL Server)' },
|
|
216
|
+
{ pattern: /^load_file\s*\(/i, description: 'LOAD_FILE()' },
|
|
217
|
+
{ pattern: /^into\s+outfile/i, description: 'INTO OUTFILE' },
|
|
218
|
+
{ pattern: /^grant\s+/i, description: 'GRANT' },
|
|
219
|
+
{ pattern: /^revoke\s+/i, description: 'REVOKE' },
|
|
220
|
+
];
|
|
221
|
+
// ==================== Database-Specific Rules ====================
|
|
222
|
+
/** PostgreSQL-specific dangerous patterns */
|
|
223
|
+
exports.POSTGRESQL_RULES = [
|
|
224
|
+
{ pattern: /^DROP\s+IF\s+EXISTS\s+(TABLE|DATABASE|SCHEMA)/i, description: 'PostgreSQL: DROP IF EXISTS' },
|
|
225
|
+
{ pattern: /^DROP\s+OWNED\s+BY/i, description: 'PostgreSQL: DROP OWNED BY' },
|
|
226
|
+
{ pattern: /^DROP\s+ROLE/i, description: 'PostgreSQL: DROP ROLE' },
|
|
227
|
+
{ pattern: /^DROP\s+CONVERSION/i, description: 'PostgreSQL: DROP CONVERSION' },
|
|
228
|
+
{ pattern: /^DROP\s+LANGUAGE/i, description: 'PostgreSQL: DROP LANGUAGE' },
|
|
229
|
+
{ pattern: /^DROP\s+EXTENSION/i, description: 'PostgreSQL: DROP EXTENSION' },
|
|
230
|
+
{ pattern: /^CREATE\s+LANGUAGE/i, description: 'PostgreSQL: CREATE LANGUAGE' },
|
|
231
|
+
{ pattern: /^CREATE\s+OR\s+REPLACE\s+(FUNCTION|PROCEDURE)/i, description: 'PostgreSQL: CREATE OR REPLACE' },
|
|
232
|
+
{ pattern: /^ALTER\s+DEFAULT\s+PRIVILEGES/i, description: 'PostgreSQL: ALTER DEFAULT PRIVILEGES' },
|
|
233
|
+
{ pattern: /^COPY\s+/i, description: 'PostgreSQL: COPY command' },
|
|
234
|
+
{ pattern: /^pg_read_file/i, description: 'PostgreSQL: pg_read_file()' },
|
|
235
|
+
{ pattern: /^pg_write_file/i, description: 'PostgreSQL: pg_write_file()' },
|
|
236
|
+
{ pattern: /^pg_execute_server_program/i, description: 'PostgreSQL: pg_execute_server_program' },
|
|
237
|
+
{ pattern: /^lo_import/i, description: 'PostgreSQL: lo_import()' },
|
|
238
|
+
{ pattern: /^lo_export/i, description: 'PostgreSQL: lo_export()' },
|
|
239
|
+
{ pattern: /^SET\s+session\s+authorization/i, description: 'PostgreSQL: SET SESSION AUTHORIZATION' },
|
|
240
|
+
{ pattern: /^RESET\s+(session\s+)?authorization/i, description: 'PostgreSQL: RESET AUTHORIZATION' },
|
|
241
|
+
];
|
|
242
|
+
/** MySQL-specific dangerous patterns */
|
|
243
|
+
exports.MYSQL_RULES = [
|
|
244
|
+
{ pattern: /^DROP\s+IF\s+EXISTS/i, description: 'MySQL: DROP IF EXISTS' },
|
|
245
|
+
{ pattern: /^DROP\s+PROCEDURE/i, description: 'MySQL: DROP PROCEDURE' },
|
|
246
|
+
{ pattern: /^DROP\s+FUNCTION/i, description: 'MySQL: DROP FUNCTION' },
|
|
247
|
+
{ pattern: /^DROP\s+TRIGGER/i, description: 'MySQL: DROP TRIGGER' },
|
|
248
|
+
{ pattern: /^CREATE\s+PROCEDURE/i, description: 'MySQL: CREATE PROCEDURE' },
|
|
249
|
+
{ pattern: /^CREATE\s+FUNCTION/i, description: 'MySQL: CREATE FUNCTION' },
|
|
250
|
+
{ pattern: /^CREATE\s+TRIGGER/i, description: 'MySQL: CREATE TRIGGER' },
|
|
251
|
+
{ pattern: /^HANDLER\s+\w+\s+OPEN/i, description: 'MySQL: HANDLER OPEN' },
|
|
252
|
+
{ pattern: /^LOAD\s+DATA\s+INFILE/i, description: 'MySQL: LOAD DATA INFILE' },
|
|
253
|
+
{ pattern: /^SELECT\s+.*\s+INTO\s+OUTFILE/i, description: 'MySQL: SELECT INTO OUTFILE' },
|
|
254
|
+
{ pattern: /^SET\s+GLOBAL\s+\w+/i, description: 'MySQL: SET GLOBAL' },
|
|
255
|
+
{ pattern: /^SET\s+@@global\./i, description: 'MySQL: SET @@global' },
|
|
256
|
+
{ pattern: /^KILL\s+(CONNECTION|QUERY)?/i, description: 'MySQL: KILL' },
|
|
257
|
+
{ pattern: /^SHUTDOWN/i, description: 'MySQL: SHUTDOWN' },
|
|
258
|
+
{ pattern: /^BINLOG\s+/i, description: 'MySQL: BINLOG' },
|
|
259
|
+
{ pattern: /^SHOW\s+BINLOG/i, description: 'MySQL: SHOW BINLOG' },
|
|
260
|
+
];
|
|
261
|
+
/** MariaDB-specific dangerous patterns */
|
|
262
|
+
exports.MARIADB_RULES = [
|
|
263
|
+
...exports.MYSQL_RULES,
|
|
264
|
+
{ pattern: /^CREATE\s+OR\s+REPLACE\s+VIEW/i, description: 'MariaDB: CREATE OR REPLACE VIEW' },
|
|
265
|
+
{ pattern: /^CREATE\s+EVENT/i, description: 'MariaDB: CREATE EVENT' },
|
|
266
|
+
{ pattern: /^DROP\s+EVENT/i, description: 'MariaDB: DROP EVENT' },
|
|
267
|
+
{ pattern: /^SET\s+GLOBAL\s+sql_mode/i, description: 'MariaDB: SET GLOBAL sql_mode' },
|
|
268
|
+
{ pattern: /^SET\s+@@session\.\w+/i, description: 'MariaDB: SET SESSION' },
|
|
269
|
+
];
|
|
270
|
+
/** Microsoft SQL Server-specific dangerous patterns */
|
|
271
|
+
exports.MSSQL_RULES = [
|
|
272
|
+
{ pattern: /^DROP\s+IF\s+EXISTS/i, description: 'MSSQL: DROP IF EXISTS' },
|
|
273
|
+
{ pattern: /^DROP\s+PROCEDURE/i, description: 'MSSQL: DROP PROCEDURE' },
|
|
274
|
+
{ pattern: /^DROP\s+FUNCTION/i, description: 'MSSQL: DROP FUNCTION' },
|
|
275
|
+
{ pattern: /^DROP\s+TRIGGER/i, description: 'MSSQL: DROP TRIGGER' },
|
|
276
|
+
{ pattern: /^CREATE\s+PROCEDURE/i, description: 'MSSQL: CREATE PROCEDURE' },
|
|
277
|
+
{ pattern: /^CREATE\s+FUNCTION/i, description: 'MSSQL: CREATE FUNCTION' },
|
|
278
|
+
{ pattern: /^CREATE\s+TRIGGER/i, description: 'MSSQL: CREATE TRIGGER' },
|
|
279
|
+
{ pattern: /^xp_cmdshell/i, description: 'MSSQL: xp_cmdshell' },
|
|
280
|
+
{ pattern: /^sp_executesql/i, description: 'MSSQL: sp_executesql' },
|
|
281
|
+
{ pattern: /^sp_addjob/i, description: 'MSSQL: sp_addjob' },
|
|
282
|
+
{ pattern: /^sp_oacreate/i, description: 'MSSQL: sp_oacreate' },
|
|
283
|
+
{ pattern: /^sp_oamethod/i, description: 'MSSQL: sp_oamethod' },
|
|
284
|
+
{ pattern: /^OPENROWSET/i, description: 'MSSQL: OPENROWSET' },
|
|
285
|
+
{ pattern: /^OPENDATASOURCE/i, description: 'MSSQL: OPENDATASOURCE' },
|
|
286
|
+
{ pattern: /^BACKUP\s+LOG/i, description: 'MSSQL: BACKUP LOG' },
|
|
287
|
+
{ pattern: /^RESTORE\s+LOG/i, description: 'MSSQL: RESTORE LOG' },
|
|
288
|
+
{ pattern: /^ALTER\s+LOGIN/i, description: 'MSSQL: ALTER LOGIN' },
|
|
289
|
+
{ pattern: /^CREATE\s+LOGIN/i, description: 'MSSQL: CREATE LOGIN' },
|
|
290
|
+
{ pattern: /^DROP\s+LOGIN/i, description: 'MSSQL: DROP LOGIN' },
|
|
291
|
+
{ pattern: /^EXEC\s+at\s+\w+/i, description: 'MSSQL: Linked server execution' },
|
|
292
|
+
{ pattern: /^EXEC\s+sp_executesql/i, description: 'MSSQL: sp_executesql' },
|
|
293
|
+
];
|
|
294
|
+
/** Oracle-specific dangerous patterns */
|
|
295
|
+
exports.ORACLE_RULES = [
|
|
296
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE|SCHEMA)\s+\w+/i, description: 'Oracle: DROP object' },
|
|
297
|
+
{ pattern: /^DROP\s+(PROCEDURE|FUNCTION|PACKAGE|TRIGGER|VIEW|SYNONYM)/i, description: 'Oracle: DROP object' },
|
|
298
|
+
{ pattern: /^CREATE\s+(OR\s+REPLACE\s+)?(PROCEDURE|FUNCTION|PACKAGE|TRIGGER|VIEW)/i, description: 'Oracle: CREATE object' },
|
|
299
|
+
{ pattern: /^DROP\s+USER\s+CASCADE/i, description: 'Oracle: DROP USER CASCADE' },
|
|
300
|
+
{ pattern: /^DROP\s+INDEX/i, description: 'Oracle: DROP INDEX' },
|
|
301
|
+
{ pattern: /^GRANT\s+\w+\s+TO\s+\w+/i, description: 'Oracle: GRANT' },
|
|
302
|
+
{ pattern: /^REVOKE\s+\w+\s+FROM\s+\w+/i, description: 'Oracle: REVOKE' },
|
|
303
|
+
{ pattern: /^BEGIN\s+.*\s+EXECUTE\s+IMMEDIATE/i, description: 'Oracle: EXECUTE IMMEDIATE' },
|
|
304
|
+
{ pattern: /^SELECT\s+ANY\s+TABLE/i, description: 'Oracle: SELECT ANY TABLE' },
|
|
305
|
+
{ pattern: /^SELECT\s+INTO\s+\w+\s+FROM\s+v\$/i, description: 'Oracle: V$ tables access' },
|
|
306
|
+
{ pattern: /^DBMS_JAVA/i, description: 'Oracle: DBMS_JAVA' },
|
|
307
|
+
{ pattern: /^DBMS_SCHEDULER/i, description: 'Oracle: DBMS_SCHEDULER' },
|
|
308
|
+
{ pattern: /^DBMS_OUTPUT/i, description: 'Oracle: DBMS_OUTPUT' },
|
|
309
|
+
{ pattern: /^UTL_FILE/i, description: 'Oracle: UTL_FILE' },
|
|
310
|
+
{ pattern: /^UTL_HTTP/i, description: 'Oracle: UTL_HTTP' },
|
|
311
|
+
{ pattern: /^DBMS_XMLGEN/i, description: 'Oracle: DBMS_XMLGEN' },
|
|
312
|
+
{ pattern: /^CREATE\s+PROFILE/i, description: 'Oracle: CREATE PROFILE' },
|
|
313
|
+
{ pattern: /^ALTER\s+PROFILE/i, description: 'Oracle: ALTER PROFILE' },
|
|
314
|
+
{ pattern: /^DROP\s+PROFILE/i, description: 'Oracle: DROP PROFILE' },
|
|
315
|
+
];
|
|
316
|
+
/** SQLite-specific dangerous patterns */
|
|
317
|
+
exports.SQLITE_RULES = [
|
|
318
|
+
{ pattern: /^DROP\s+(TABLE|DATABASE|INDEX|TRIGGER|VIEW)\s+\w+/i, description: 'SQLite: DROP object' },
|
|
319
|
+
{ pattern: /^DETACH\s+DATABASE/i, description: 'SQLite: DETACH DATABASE' },
|
|
320
|
+
{ pattern: /^ATTACH\s+DATABASE/i, description: 'SQLite: ATTACH DATABASE' },
|
|
321
|
+
{ pattern: /\.mode\s+\w+/i, description: 'SQLite: .mode command' },
|
|
322
|
+
{ pattern: /\.import\s+/i, description: 'SQLite: .import command' },
|
|
323
|
+
{ pattern: /\.dump\s+/i, description: 'SQLite: .dump command' },
|
|
324
|
+
];
|
|
325
|
+
/** All database-specific rules */
|
|
326
|
+
exports.DATABASE_RULES = {
|
|
327
|
+
postgresql: exports.POSTGRESQL_RULES,
|
|
328
|
+
mysql: exports.MYSQL_RULES,
|
|
329
|
+
mariadb: exports.MARIADB_RULES,
|
|
330
|
+
mssql: exports.MSSQL_RULES,
|
|
331
|
+
oracle: exports.ORACLE_RULES,
|
|
332
|
+
sqlite: exports.SQLITE_RULES,
|
|
333
|
+
};
|
|
334
|
+
// ==================== Query Firewall Class ====================
|
|
335
|
+
class QueryFirewall {
|
|
336
|
+
constructor(prorm, options = {}) {
|
|
337
|
+
this.allowRules = [];
|
|
338
|
+
this.blockRules = [];
|
|
339
|
+
this.blockedHistory = [];
|
|
340
|
+
this.queryLog = [];
|
|
341
|
+
this.originalQuery = null;
|
|
342
|
+
this.isActive = false;
|
|
343
|
+
this.prorm = prorm;
|
|
344
|
+
this.options = {
|
|
345
|
+
mode: options.mode ?? 'allowlist',
|
|
346
|
+
blockDangerousByDefault: options.blockDangerousByDefault ?? true,
|
|
347
|
+
onBlock: options.onBlock ?? (() => { }),
|
|
348
|
+
onAllow: options.onAllow ?? (() => { }),
|
|
349
|
+
maxHistory: options.maxHistory ?? 1000,
|
|
350
|
+
logAllQueries: options.logAllQueries ?? false,
|
|
351
|
+
caseInsensitive: options.caseInsensitive ?? true,
|
|
352
|
+
rules: options.rules ?? [],
|
|
353
|
+
frameworks: options.frameworks ?? [],
|
|
354
|
+
};
|
|
355
|
+
// Initialize with default rules
|
|
356
|
+
this.initializeRules();
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Create a new firewall instance (factory method)
|
|
360
|
+
*/
|
|
361
|
+
static create(prorm, options) {
|
|
362
|
+
const instance = new QueryFirewall(prorm, options);
|
|
363
|
+
QueryFirewall.instances.set(prorm, instance);
|
|
364
|
+
return instance;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Get existing instance for a Prorm instance
|
|
368
|
+
*/
|
|
369
|
+
static getInstance(prorm) {
|
|
370
|
+
return QueryFirewall.instances.get(prorm);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Initialize default rules
|
|
374
|
+
*/
|
|
375
|
+
initializeRules() {
|
|
376
|
+
// Add dangerous patterns if configured
|
|
377
|
+
if (this.options.blockDangerousByDefault) {
|
|
378
|
+
for (const rule of DEFAULT_DANGEROUS_PATTERNS) {
|
|
379
|
+
this.blockRules.push({
|
|
380
|
+
id: `default-block-${this.blockRules.length}`,
|
|
381
|
+
type: 'block',
|
|
382
|
+
pattern: rule.pattern,
|
|
383
|
+
description: rule.description,
|
|
384
|
+
createdAt: new Date(),
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Add OWASP SQL injection patterns
|
|
389
|
+
if (this.options.frameworks.includes('OWASP') || this.options.blockDangerousByDefault) {
|
|
390
|
+
for (const pattern of exports.OWASP_SQL_INJECTION_PATTERNS) {
|
|
391
|
+
this.blockRules.push({
|
|
392
|
+
id: `owasp-sql-injection-${this.blockRules.length}`,
|
|
393
|
+
type: 'block',
|
|
394
|
+
pattern,
|
|
395
|
+
description: 'OWASP SQL Injection pattern',
|
|
396
|
+
createdAt: new Date(),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
// CIS Benchmarks - require explicit allow for data modification
|
|
401
|
+
if (this.options.frameworks.includes('CIS')) {
|
|
402
|
+
// Add strict block rules for CIS compliance
|
|
403
|
+
this.blockRules.push({
|
|
404
|
+
id: 'cis-drop-all',
|
|
405
|
+
type: 'block',
|
|
406
|
+
pattern: /^DROP\s+(TABLE|DATABASE)/i,
|
|
407
|
+
description: 'CIS: Block DROP DATABASE/TABLE',
|
|
408
|
+
createdAt: new Date(),
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
// Add custom rules
|
|
412
|
+
for (const rule of this.options.rules) {
|
|
413
|
+
if (rule.type === 'allow') {
|
|
414
|
+
this.allow(rule.pattern, rule.description);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
this.block(rule.pattern, rule.description);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Add an allowed pattern
|
|
423
|
+
*/
|
|
424
|
+
allow(pattern, description) {
|
|
425
|
+
const rule = {
|
|
426
|
+
id: `allow-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
427
|
+
type: 'allow',
|
|
428
|
+
pattern,
|
|
429
|
+
description,
|
|
430
|
+
createdAt: new Date(),
|
|
431
|
+
};
|
|
432
|
+
this.allowRules.push(rule);
|
|
433
|
+
return rule;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Add a blocked pattern
|
|
437
|
+
*/
|
|
438
|
+
block(pattern, description) {
|
|
439
|
+
const rule = {
|
|
440
|
+
id: `block-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
441
|
+
type: 'block',
|
|
442
|
+
pattern,
|
|
443
|
+
description,
|
|
444
|
+
createdAt: new Date(),
|
|
445
|
+
};
|
|
446
|
+
this.blockRules.push(rule);
|
|
447
|
+
return rule;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Remove a rule by ID
|
|
451
|
+
*/
|
|
452
|
+
removeRule(ruleId) {
|
|
453
|
+
const allowIndex = this.allowRules.findIndex(r => r.id === ruleId);
|
|
454
|
+
if (allowIndex !== -1) {
|
|
455
|
+
this.allowRules.splice(allowIndex, 1);
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
const blockIndex = this.blockRules.findIndex(r => r.id === ruleId);
|
|
459
|
+
if (blockIndex !== -1) {
|
|
460
|
+
this.blockRules.splice(blockIndex, 1);
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Check if a query is allowed
|
|
467
|
+
*/
|
|
468
|
+
checkQuery(sql) {
|
|
469
|
+
const analysis = this.analyzeQuery(sql);
|
|
470
|
+
// Apply firewall mode logic
|
|
471
|
+
let allowed = true;
|
|
472
|
+
let reason;
|
|
473
|
+
let matchedRule;
|
|
474
|
+
// Block rules (populated by blockDangerousByDefault / explicit addBlockRule calls)
|
|
475
|
+
// are a safety net that applies regardless of mode - including 'allowlist' mode.
|
|
476
|
+
// Previously this check only ran for 'blocklist'/'strict', so 'allowlist' mode
|
|
477
|
+
// with zero configured allow rules (the out-of-the-box default) silently allowed
|
|
478
|
+
// every query, dangerous or not.
|
|
479
|
+
const matchedBlock = this.blockRules.find(rule => this.matchPattern(sql, rule.pattern));
|
|
480
|
+
if (matchedBlock) {
|
|
481
|
+
allowed = false;
|
|
482
|
+
reason = `Query matches blocked pattern: ${matchedBlock.description || matchedBlock.pattern}`;
|
|
483
|
+
matchedRule = matchedBlock.id;
|
|
484
|
+
}
|
|
485
|
+
if (allowed && (this.options.mode === 'allowlist' || this.options.mode === 'strict')) {
|
|
486
|
+
// In allowlist/strict mode, an explicitly-configured allowlist means the query
|
|
487
|
+
// must match at least one allow rule. With zero allow rules configured, fail
|
|
488
|
+
// closed (deny) rather than silently allowing everything - a firewall that
|
|
489
|
+
// allows all traffic when unconfigured isn't a firewall.
|
|
490
|
+
const matchesAllow = this.allowRules.some(rule => this.matchPattern(sql, rule.pattern));
|
|
491
|
+
if (!matchesAllow) {
|
|
492
|
+
allowed = false;
|
|
493
|
+
reason =
|
|
494
|
+
this.allowRules.length > 0
|
|
495
|
+
? 'Query does not match any allowed pattern'
|
|
496
|
+
: 'No allow rules configured - denying by default (fail closed)';
|
|
497
|
+
matchedRule = this.options.mode === 'strict' ? 'strict-allow' : 'allowlist';
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// Update analysis result
|
|
501
|
+
analysis.allowed = allowed;
|
|
502
|
+
analysis.reason = reason;
|
|
503
|
+
analysis.matchedRule = matchedRule;
|
|
504
|
+
// Handle blocked query
|
|
505
|
+
if (!allowed) {
|
|
506
|
+
const blocked = {
|
|
507
|
+
id: `blocked-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
508
|
+
timestamp: new Date(),
|
|
509
|
+
query: sql,
|
|
510
|
+
reason: reason || 'Query blocked',
|
|
511
|
+
matchedRule: matchedRule || 'unknown',
|
|
512
|
+
operation: analysis.operation,
|
|
513
|
+
};
|
|
514
|
+
this.blockedHistory.push(blocked);
|
|
515
|
+
// Trim history
|
|
516
|
+
if (this.blockedHistory.length > this.options.maxHistory) {
|
|
517
|
+
this.blockedHistory = this.blockedHistory.slice(-this.options.maxHistory);
|
|
518
|
+
}
|
|
519
|
+
// Callback
|
|
520
|
+
this.options.onBlock(blocked);
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
// Callback for allowed query
|
|
524
|
+
this.options.onAllow(analysis);
|
|
525
|
+
}
|
|
526
|
+
// Log if enabled
|
|
527
|
+
if (this.options.logAllQueries) {
|
|
528
|
+
this.queryLog.push(analysis);
|
|
529
|
+
if (this.queryLog.length > this.options.maxHistory) {
|
|
530
|
+
this.queryLog = this.queryLog.slice(-this.options.maxHistory);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return analysis;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Analyze a query without applying firewall rules
|
|
537
|
+
*/
|
|
538
|
+
analyzeQuery(sql) {
|
|
539
|
+
const upperQuery = sql.toUpperCase();
|
|
540
|
+
let operation = 'UNKNOWN';
|
|
541
|
+
if (upperQuery.includes('SELECT'))
|
|
542
|
+
operation = 'SELECT';
|
|
543
|
+
else if (upperQuery.includes('INSERT'))
|
|
544
|
+
operation = 'INSERT';
|
|
545
|
+
else if (upperQuery.includes('UPDATE'))
|
|
546
|
+
operation = 'UPDATE';
|
|
547
|
+
else if (upperQuery.includes('DELETE'))
|
|
548
|
+
operation = 'DELETE';
|
|
549
|
+
else if (upperQuery.includes('DROP'))
|
|
550
|
+
operation = 'DROP';
|
|
551
|
+
else if (upperQuery.includes('TRUNCATE'))
|
|
552
|
+
operation = 'TRUNCATE';
|
|
553
|
+
else if (upperQuery.includes('ALTER'))
|
|
554
|
+
operation = 'ALTER';
|
|
555
|
+
else if (upperQuery.includes('CREATE'))
|
|
556
|
+
operation = 'CREATE';
|
|
557
|
+
else if (upperQuery.includes('EXEC') || upperQuery.includes('CALL'))
|
|
558
|
+
operation = 'EXECUTE';
|
|
559
|
+
// Extract table names
|
|
560
|
+
const tableMatches = sql.match(/(?:from|into|update|table)\s+`?(\w+)`?/gi) || [];
|
|
561
|
+
const tables = tableMatches
|
|
562
|
+
.map(m => m.replace(/`/g, '').split(' ').pop() || '')
|
|
563
|
+
.filter(Boolean);
|
|
564
|
+
// Check for sensitive data patterns
|
|
565
|
+
const sensitivePatterns = [
|
|
566
|
+
{ name: 'SSN', pattern: /\b\d{3}-\d{2}-\d{4}\b/ },
|
|
567
|
+
{ name: 'Credit Card', pattern: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/ },
|
|
568
|
+
{ name: 'Email', pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/ },
|
|
569
|
+
];
|
|
570
|
+
const containsSensitiveData = sensitivePatterns.some(s => s.pattern.test(sql));
|
|
571
|
+
// Calculate risk score
|
|
572
|
+
let riskScore = 0;
|
|
573
|
+
if (operation === 'DROP' || operation === 'TRUNCATE')
|
|
574
|
+
riskScore += 80;
|
|
575
|
+
if (operation === 'DELETE')
|
|
576
|
+
riskScore += 50;
|
|
577
|
+
if (operation === 'UPDATE')
|
|
578
|
+
riskScore += 30;
|
|
579
|
+
if (containsSensitiveData)
|
|
580
|
+
riskScore += 20;
|
|
581
|
+
if (sql.length > 1000)
|
|
582
|
+
riskScore += 10;
|
|
583
|
+
// Check for SQL injection patterns
|
|
584
|
+
for (const pattern of exports.OWASP_SQL_INJECTION_PATTERNS) {
|
|
585
|
+
if (pattern.test(sql)) {
|
|
586
|
+
riskScore += 50;
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
query: sql,
|
|
592
|
+
allowed: true,
|
|
593
|
+
operation,
|
|
594
|
+
tables,
|
|
595
|
+
containsSensitiveData,
|
|
596
|
+
riskScore,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Match a pattern against a query
|
|
601
|
+
*/
|
|
602
|
+
matchPattern(query, pattern) {
|
|
603
|
+
try {
|
|
604
|
+
if (pattern instanceof RegExp) {
|
|
605
|
+
return this.options.caseInsensitive
|
|
606
|
+
? pattern.test(query)
|
|
607
|
+
: new RegExp(pattern.source, pattern.flags.replace('i', '') + (this.options.caseInsensitive ? 'i' : '')).test(query);
|
|
608
|
+
}
|
|
609
|
+
// Convert string to regex
|
|
610
|
+
const flags = this.options.caseInsensitive ? 'i' : '';
|
|
611
|
+
const regex = new RegExp(pattern, flags);
|
|
612
|
+
return regex.test(query);
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Get blocked query history
|
|
620
|
+
*/
|
|
621
|
+
getBlockedHistory(options) {
|
|
622
|
+
let filtered = [...this.blockedHistory];
|
|
623
|
+
if (options?.since) {
|
|
624
|
+
filtered = filtered.filter(b => b.timestamp >= options.since);
|
|
625
|
+
}
|
|
626
|
+
if (options?.operation) {
|
|
627
|
+
filtered = filtered.filter(b => b.operation === options.operation);
|
|
628
|
+
}
|
|
629
|
+
if (options?.limit) {
|
|
630
|
+
filtered = filtered.slice(-options.limit);
|
|
631
|
+
}
|
|
632
|
+
return filtered;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Get query log (if logging enabled)
|
|
636
|
+
*/
|
|
637
|
+
getQueryLog(limit) {
|
|
638
|
+
const log = [...this.queryLog];
|
|
639
|
+
return limit ? log.slice(-limit) : log;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Get all rules
|
|
643
|
+
*/
|
|
644
|
+
getRules() {
|
|
645
|
+
return {
|
|
646
|
+
allowRules: [...this.allowRules],
|
|
647
|
+
blockRules: [...this.blockRules],
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Get statistics
|
|
652
|
+
*/
|
|
653
|
+
getStats() {
|
|
654
|
+
const blockedByOperation = {
|
|
655
|
+
SELECT: 0,
|
|
656
|
+
INSERT: 0,
|
|
657
|
+
UPDATE: 0,
|
|
658
|
+
DELETE: 0,
|
|
659
|
+
DROP: 0,
|
|
660
|
+
TRUNCATE: 0,
|
|
661
|
+
ALTER: 0,
|
|
662
|
+
CREATE: 0,
|
|
663
|
+
EXECUTE: 0,
|
|
664
|
+
UNKNOWN: 0,
|
|
665
|
+
};
|
|
666
|
+
for (const blocked of this.blockedHistory) {
|
|
667
|
+
blockedByOperation[blocked.operation]++;
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
totalBlocked: this.blockedHistory.length,
|
|
671
|
+
totalAllowed: this.queryLog.filter(q => q.allowed).length,
|
|
672
|
+
blockedByOperation,
|
|
673
|
+
allowRulesCount: this.allowRules.length,
|
|
674
|
+
blockRulesCount: this.blockRules.length,
|
|
675
|
+
isActive: this.isActive,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Apply CIS Benchmarks rules
|
|
680
|
+
*/
|
|
681
|
+
applyCisRules() {
|
|
682
|
+
for (const rule of exports.CIS_BENCHMARK_RULES) {
|
|
683
|
+
this.block(rule.pattern, rule.description);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Apply PCI DSS rules
|
|
688
|
+
*/
|
|
689
|
+
applyPciRules() {
|
|
690
|
+
for (const rule of exports.PCI_DSS_RULES) {
|
|
691
|
+
this.block(rule.pattern, rule.description);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Apply HIPAA rules
|
|
696
|
+
*/
|
|
697
|
+
applyHipaaRules() {
|
|
698
|
+
for (const rule of exports.HIPAA_RULES) {
|
|
699
|
+
this.block(rule.pattern, rule.description);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Apply SOX rules
|
|
704
|
+
*/
|
|
705
|
+
applySoxRules() {
|
|
706
|
+
for (const rule of exports.SOX_RULES) {
|
|
707
|
+
this.block(rule.pattern, rule.description);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Apply NIST CSF rules
|
|
712
|
+
*/
|
|
713
|
+
applyNistRules() {
|
|
714
|
+
for (const rule of exports.NIST_CSF_RULES) {
|
|
715
|
+
this.block(rule.pattern, rule.description);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Apply OWASP SQL Injection patterns
|
|
720
|
+
*/
|
|
721
|
+
applyOwaspRules() {
|
|
722
|
+
for (const pattern of exports.OWASP_SQL_INJECTION_PATTERNS) {
|
|
723
|
+
this.block(pattern, 'OWASP SQL Injection pattern');
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Apply all destructive operation rules
|
|
728
|
+
*/
|
|
729
|
+
applyDestructiveRules() {
|
|
730
|
+
for (const rule of exports.FIREWALL_RULES.DESTRUCTIVE) {
|
|
731
|
+
this.block(rule.pattern, rule.description);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Apply all schema modification rules
|
|
736
|
+
*/
|
|
737
|
+
applySchemaRules() {
|
|
738
|
+
for (const rule of exports.FIREWALL_RULES.SCHEMA_MODIFICATION) {
|
|
739
|
+
this.block(rule.pattern, rule.description);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Apply all permission rules
|
|
744
|
+
*/
|
|
745
|
+
applyPermissionRules() {
|
|
746
|
+
for (const rule of exports.FIREWALL_RULES.PERMISSIONS) {
|
|
747
|
+
this.block(rule.pattern, rule.description);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Apply all file operation rules
|
|
752
|
+
*/
|
|
753
|
+
applyFileRules() {
|
|
754
|
+
for (const rule of exports.FIREWALL_RULES.FILE_OPERATIONS) {
|
|
755
|
+
this.block(rule.pattern, rule.description);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Apply all information gathering rules
|
|
760
|
+
*/
|
|
761
|
+
applyInfoGatheringRules() {
|
|
762
|
+
for (const rule of exports.FIREWALL_RULES.INFORMATION_GATHERING) {
|
|
763
|
+
this.block(rule.pattern, rule.description);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Apply all code execution rules
|
|
768
|
+
*/
|
|
769
|
+
applyCodeExecutionRules() {
|
|
770
|
+
for (const rule of exports.FIREWALL_RULES.CODE_EXECUTION) {
|
|
771
|
+
this.block(rule.pattern, rule.description);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Apply all transaction control rules
|
|
776
|
+
*/
|
|
777
|
+
applyTransactionRules() {
|
|
778
|
+
for (const rule of exports.FIREWALL_RULES.TRANSACTION_CONTROL) {
|
|
779
|
+
this.block(rule.pattern, rule.description);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Apply all remote operation rules
|
|
784
|
+
*/
|
|
785
|
+
applyRemoteOperationRules() {
|
|
786
|
+
for (const rule of exports.FIREWALL_RULES.REMOTE_OPERATIONS) {
|
|
787
|
+
this.block(rule.pattern, rule.description);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Apply all crypto operation rules
|
|
792
|
+
*/
|
|
793
|
+
applyCryptoRules() {
|
|
794
|
+
for (const rule of exports.FIREWALL_RULES.CRYPTO_OPERATIONS) {
|
|
795
|
+
this.block(rule.pattern, rule.description);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Apply all backup operation rules
|
|
800
|
+
*/
|
|
801
|
+
applyBackupRules() {
|
|
802
|
+
for (const rule of exports.FIREWALL_RULES.BACKUP_OPERATIONS) {
|
|
803
|
+
this.block(rule.pattern, rule.description);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Apply database-specific rules
|
|
808
|
+
* @param database - 'postgresql' | 'mysql' | 'mariadb' | 'mssql' | 'oracle' | 'sqlite'
|
|
809
|
+
*/
|
|
810
|
+
applyDatabaseRules(database) {
|
|
811
|
+
const rules = exports.DATABASE_RULES[database];
|
|
812
|
+
if (rules) {
|
|
813
|
+
for (const rule of rules) {
|
|
814
|
+
this.block(rule.pattern, `${database.toUpperCase()}: ${rule.description}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Apply all predefined security rules
|
|
820
|
+
*/
|
|
821
|
+
applyAllSecurityRules() {
|
|
822
|
+
this.applyDestructiveRules();
|
|
823
|
+
this.applySchemaRules();
|
|
824
|
+
this.applyPermissionRules();
|
|
825
|
+
this.applyFileRules();
|
|
826
|
+
this.applyInfoGatheringRules();
|
|
827
|
+
this.applyCodeExecutionRules();
|
|
828
|
+
this.applyTransactionRules();
|
|
829
|
+
this.applyRemoteOperationRules();
|
|
830
|
+
this.applyCryptoRules();
|
|
831
|
+
this.applyBackupRules();
|
|
832
|
+
this.applyOwaspRules();
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Apply predefined compliance framework rules
|
|
836
|
+
* @param framework - 'CIS', 'PCI', 'HIPAA', 'SOX', 'NIST', 'OWASP'
|
|
837
|
+
*/
|
|
838
|
+
applyFrameworkRules(framework) {
|
|
839
|
+
switch (framework) {
|
|
840
|
+
case 'CIS':
|
|
841
|
+
this.applyCisRules();
|
|
842
|
+
break;
|
|
843
|
+
case 'PCI':
|
|
844
|
+
this.applyPciRules();
|
|
845
|
+
break;
|
|
846
|
+
case 'HIPAA':
|
|
847
|
+
this.applyHipaaRules();
|
|
848
|
+
break;
|
|
849
|
+
case 'SOX':
|
|
850
|
+
this.applySoxRules();
|
|
851
|
+
break;
|
|
852
|
+
case 'NIST':
|
|
853
|
+
this.applyNistRules();
|
|
854
|
+
break;
|
|
855
|
+
case 'OWASP':
|
|
856
|
+
this.applyOwaspRules();
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Start the firewall - intercept queries
|
|
862
|
+
*/
|
|
863
|
+
start() {
|
|
864
|
+
if (this.isActive)
|
|
865
|
+
return;
|
|
866
|
+
this.isActive = true;
|
|
867
|
+
this.originalQuery = this.prorm.query;
|
|
868
|
+
this.prorm.query = async (...args) => {
|
|
869
|
+
const query = typeof args[0] === 'string' ? args[0] : args[0]?.sql ?? '';
|
|
870
|
+
if (query.trim()) {
|
|
871
|
+
const analysis = this.checkQuery(query);
|
|
872
|
+
if (!analysis.allowed) {
|
|
873
|
+
throw new Error(`Query blocked by firewall: ${analysis.reason}`);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return this.originalQuery.apply(this.prorm, args);
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Stop the firewall - restore original query method
|
|
881
|
+
*/
|
|
882
|
+
stop() {
|
|
883
|
+
if (!this.isActive)
|
|
884
|
+
return;
|
|
885
|
+
this.isActive = false;
|
|
886
|
+
if (this.originalQuery) {
|
|
887
|
+
this.prorm.query = this.originalQuery;
|
|
888
|
+
this.originalQuery = null;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Check if firewall is active
|
|
893
|
+
*/
|
|
894
|
+
isFirewallActive() {
|
|
895
|
+
return this.isActive;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Clear blocked history
|
|
899
|
+
*/
|
|
900
|
+
clearHistory() {
|
|
901
|
+
this.blockedHistory = [];
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Clear query log
|
|
905
|
+
*/
|
|
906
|
+
clearLog() {
|
|
907
|
+
this.queryLog = [];
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Export rules for audit purposes
|
|
911
|
+
*/
|
|
912
|
+
exportRules() {
|
|
913
|
+
return JSON.stringify({
|
|
914
|
+
allowRules: this.allowRules.map(r => ({
|
|
915
|
+
...r,
|
|
916
|
+
pattern: r.pattern instanceof RegExp ? r.pattern.source : r.pattern,
|
|
917
|
+
})),
|
|
918
|
+
blockRules: this.blockRules.map(r => ({
|
|
919
|
+
...r,
|
|
920
|
+
pattern: r.pattern instanceof RegExp ? r.pattern.source : r.pattern,
|
|
921
|
+
})),
|
|
922
|
+
}, null, 2);
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Import rules from JSON
|
|
926
|
+
*/
|
|
927
|
+
importRules(json) {
|
|
928
|
+
const data = JSON.parse(json);
|
|
929
|
+
if (data.allowRules) {
|
|
930
|
+
for (const rule of data.allowRules) {
|
|
931
|
+
this.allow(rule.pattern instanceof RegExp ? rule.pattern.source : rule.pattern, rule.description);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (data.blockRules) {
|
|
935
|
+
for (const rule of data.blockRules) {
|
|
936
|
+
this.block(rule.pattern instanceof RegExp ? rule.pattern.source : rule.pattern, rule.description);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
exports.QueryFirewall = QueryFirewall;
|
|
942
|
+
QueryFirewall.instances = new Map();
|
|
943
|
+
// ==================== Static Helper Functions ====================
|
|
944
|
+
/**
|
|
945
|
+
* Analyze a query for security issues (static method)
|
|
946
|
+
*/
|
|
947
|
+
function analyzeQuerySecurity(query) {
|
|
948
|
+
const detectedPatterns = [];
|
|
949
|
+
// Check SQL injection patterns
|
|
950
|
+
for (const pattern of exports.OWASP_SQL_INJECTION_PATTERNS) {
|
|
951
|
+
if (pattern.test(query)) {
|
|
952
|
+
detectedPatterns.push(`SQL_INJECTION:${pattern.source}`);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
// Check for dangerous operations
|
|
956
|
+
let isDangerousOperation = false;
|
|
957
|
+
const upperQuery = query.toUpperCase();
|
|
958
|
+
if (/^DROP\s+/i.test(upperQuery) || /^TRUNCATE/i.test(upperQuery)) {
|
|
959
|
+
isDangerousOperation = true;
|
|
960
|
+
detectedPatterns.push('DANGEROUS_OPERATION:DROP_OR_TRUNCATE');
|
|
961
|
+
}
|
|
962
|
+
if (/^DELETE\s+FROM\s+\w+\s*$/i.test(upperQuery)) {
|
|
963
|
+
isDangerousOperation = true;
|
|
964
|
+
detectedPatterns.push('DANGEROUS_OPERATION:DELETE_ALL');
|
|
965
|
+
}
|
|
966
|
+
// Determine risk level
|
|
967
|
+
let riskLevel = 'low';
|
|
968
|
+
if (detectedPatterns.some(p => p.startsWith('SQL_INJECTION'))) {
|
|
969
|
+
riskLevel = 'critical';
|
|
970
|
+
}
|
|
971
|
+
else if (detectedPatterns.some(p => p.startsWith('DANGEROUS_OPERATION'))) {
|
|
972
|
+
riskLevel = 'high';
|
|
973
|
+
}
|
|
974
|
+
else if (isDangerousOperation) {
|
|
975
|
+
riskLevel = 'medium';
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
isSQLInjection: detectedPatterns.some(p => p.startsWith('SQL_INJECTION')),
|
|
979
|
+
isDangerousOperation,
|
|
980
|
+
riskLevel,
|
|
981
|
+
detectedPatterns,
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Get recommended firewall rules for compliance frameworks
|
|
986
|
+
*/
|
|
987
|
+
function getRecommendedRules(frameworks) {
|
|
988
|
+
const rules = [];
|
|
989
|
+
// OWASP recommendations
|
|
990
|
+
if (frameworks.includes('OWASP')) {
|
|
991
|
+
rules.push({ type: 'block', pattern: "('|(\\'))", description: 'OWASP: Quote escaping' }, { type: 'block', pattern: '(union\\s+select)', description: 'OWASP: UNION SELECT' }, { type: 'block', pattern: '(--)', description: 'OWASP: Comment injection' });
|
|
992
|
+
}
|
|
993
|
+
// CIS Benchmarks
|
|
994
|
+
if (frameworks.includes('CIS')) {
|
|
995
|
+
rules.push({ type: 'block', pattern: '^DROP\\s+DATABASE', description: 'CIS: Block DROP DATABASE' }, { type: 'block', pattern: '^DROP\\s+TABLE', description: 'CIS: Block DROP TABLE' }, { type: 'allow', pattern: '^SELECT', description: 'CIS: Allow SELECT queries' });
|
|
996
|
+
}
|
|
997
|
+
// GDPR specific
|
|
998
|
+
if (frameworks.includes('GDPR')) {
|
|
999
|
+
rules.push({ type: 'block', pattern: '(select\\s+.*\\s+from\\s+users)', description: 'GDPR: Limit user data queries' });
|
|
1000
|
+
}
|
|
1001
|
+
return rules;
|
|
1002
|
+
}
|
|
1003
|
+
// ==================== Class Decorators ====================
|
|
1004
|
+
/** Metadata key for firewall rules on classes */
|
|
1005
|
+
const FIREWALL_RULES_KEY = Symbol('__orm_firewall_rules__');
|
|
1006
|
+
const FIREWALL_DATABASE_KEY = Symbol('__orm_firewall_database__');
|
|
1007
|
+
/**
|
|
1008
|
+
* Decorator to apply firewall rules to a model class
|
|
1009
|
+
*
|
|
1010
|
+
* @example
|
|
1011
|
+
* // Block destructive operations and file operations on a model
|
|
1012
|
+
* @FirewallRules({
|
|
1013
|
+
* blockDestructive: true,
|
|
1014
|
+
* blockFileOperations: true,
|
|
1015
|
+
* })
|
|
1016
|
+
* class SensitiveData extends Model { ... }
|
|
1017
|
+
*
|
|
1018
|
+
* // Apply database-specific rules
|
|
1019
|
+
* @FirewallRules({
|
|
1020
|
+
* database: 'postgresql',
|
|
1021
|
+
* blockCodeExecution: true,
|
|
1022
|
+
* })
|
|
1023
|
+
* class UserDefinedFunction extends Model { ... }
|
|
1024
|
+
*
|
|
1025
|
+
* // Custom rules
|
|
1026
|
+
* @FirewallRules({
|
|
1027
|
+
* rules: [
|
|
1028
|
+
* { pattern: /^SELECT\s+FROM\s+logs/i, description: 'Block log access' },
|
|
1029
|
+
* ],
|
|
1030
|
+
* })
|
|
1031
|
+
* class AuditLog extends Model { ... }
|
|
1032
|
+
*/
|
|
1033
|
+
function FirewallRules(options = {}) {
|
|
1034
|
+
return function (target) {
|
|
1035
|
+
const rules = [];
|
|
1036
|
+
// Apply database-specific rules
|
|
1037
|
+
if (options.database) {
|
|
1038
|
+
rules.push(`database:${options.database}`);
|
|
1039
|
+
target[FIREWALL_DATABASE_KEY] = options.database;
|
|
1040
|
+
}
|
|
1041
|
+
// Apply category rules
|
|
1042
|
+
if (options.blockDestructive) {
|
|
1043
|
+
rules.push('DESTRUCTIVE');
|
|
1044
|
+
}
|
|
1045
|
+
if (options.blockSchemaModification) {
|
|
1046
|
+
rules.push('SCHEMA_MODIFICATION');
|
|
1047
|
+
}
|
|
1048
|
+
if (options.blockPermissions) {
|
|
1049
|
+
rules.push('PERMISSIONS');
|
|
1050
|
+
}
|
|
1051
|
+
if (options.blockFileOperations) {
|
|
1052
|
+
rules.push('FILE_OPERATIONS');
|
|
1053
|
+
}
|
|
1054
|
+
if (options.blockInfoGathering) {
|
|
1055
|
+
rules.push('INFORMATION_GATHERING');
|
|
1056
|
+
}
|
|
1057
|
+
if (options.blockCodeExecution) {
|
|
1058
|
+
rules.push('CODE_EXECUTION');
|
|
1059
|
+
}
|
|
1060
|
+
if (options.blockTransactions) {
|
|
1061
|
+
rules.push('TRANSACTION_CONTROL');
|
|
1062
|
+
}
|
|
1063
|
+
if (options.blockRemoteOperations) {
|
|
1064
|
+
rules.push('REMOTE_OPERATIONS');
|
|
1065
|
+
}
|
|
1066
|
+
// Store custom rules
|
|
1067
|
+
if (options.rules && options.rules.length > 0) {
|
|
1068
|
+
rules.push('custom');
|
|
1069
|
+
target[FIREWALL_RULES_KEY] = options.rules;
|
|
1070
|
+
}
|
|
1071
|
+
target[FIREWALL_RULES_KEY] = rules;
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Decorator for PostgreSQL-specific firewall rules
|
|
1076
|
+
*
|
|
1077
|
+
* @example
|
|
1078
|
+
* @PostgreSQLFirewall()
|
|
1079
|
+
* class PostgresSensitive extends Model { ... }
|
|
1080
|
+
*/
|
|
1081
|
+
function PostgreSQLFirewall() {
|
|
1082
|
+
return FirewallRules({ database: 'postgresql' });
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Decorator for MySQL-specific firewall rules
|
|
1086
|
+
*
|
|
1087
|
+
* @example
|
|
1088
|
+
* @MySQLFirewall()
|
|
1089
|
+
* class MySQLSensitive extends Model { ... }
|
|
1090
|
+
*/
|
|
1091
|
+
function MySQLFirewall() {
|
|
1092
|
+
return FirewallRules({ database: 'mysql' });
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Decorator for MariaDB-specific firewall rules
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* @MariaDBFirewall()
|
|
1099
|
+
* class MariaDBSensitive extends Model { ... }
|
|
1100
|
+
*/
|
|
1101
|
+
function MariaDBFirewall() {
|
|
1102
|
+
return FirewallRules({ database: 'mariadb' });
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Decorator for SQL Server-specific firewall rules
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* @MSSQLFirewall()
|
|
1109
|
+
* class MSSQLSensitive extends Model { ... }
|
|
1110
|
+
*/
|
|
1111
|
+
function MSSQLFirewall() {
|
|
1112
|
+
return FirewallRules({ database: 'mssql' });
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Decorator for Oracle-specific firewall rules
|
|
1116
|
+
*
|
|
1117
|
+
* @example
|
|
1118
|
+
* @OracleFirewall()
|
|
1119
|
+
* class OracleSensitive extends Model { ... }
|
|
1120
|
+
*/
|
|
1121
|
+
function OracleFirewall() {
|
|
1122
|
+
return FirewallRules({ database: 'oracle' });
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Decorator for SQLite-specific firewall rules
|
|
1126
|
+
*
|
|
1127
|
+
* @example
|
|
1128
|
+
* @SQLiteFirewall()
|
|
1129
|
+
* class SQLiteSensitive extends Model { ... }
|
|
1130
|
+
*/
|
|
1131
|
+
function SQLiteFirewall() {
|
|
1132
|
+
return FirewallRules({ database: 'sqlite' });
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Apply firewall rules from a decorated model to a firewall instance
|
|
1136
|
+
*/
|
|
1137
|
+
function applyFirewallRulesToModel(model, firewall) {
|
|
1138
|
+
const rules = model[FIREWALL_RULES_KEY];
|
|
1139
|
+
const database = model[FIREWALL_DATABASE_KEY];
|
|
1140
|
+
if (!rules && !database)
|
|
1141
|
+
return;
|
|
1142
|
+
// Apply database-specific rules
|
|
1143
|
+
if (database && exports.DATABASE_RULES[database]) {
|
|
1144
|
+
for (const rule of exports.DATABASE_RULES[database]) {
|
|
1145
|
+
firewall.block(rule.pattern, `${database.toUpperCase()}: ${rule.description}`);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
// Apply category rules
|
|
1149
|
+
if (rules && rules.includes('DESTRUCTIVE')) {
|
|
1150
|
+
firewall.applyDestructiveRules();
|
|
1151
|
+
}
|
|
1152
|
+
if (rules && rules.includes('SCHEMA_MODIFICATION')) {
|
|
1153
|
+
firewall.applySchemaRules();
|
|
1154
|
+
}
|
|
1155
|
+
if (rules && rules.includes('PERMISSIONS')) {
|
|
1156
|
+
firewall.applyPermissionRules();
|
|
1157
|
+
}
|
|
1158
|
+
if (rules && rules.includes('FILE_OPERATIONS')) {
|
|
1159
|
+
firewall.applyFileRules();
|
|
1160
|
+
}
|
|
1161
|
+
if (rules && rules.includes('INFORMATION_GATHERING')) {
|
|
1162
|
+
firewall.applyInfoGatheringRules();
|
|
1163
|
+
}
|
|
1164
|
+
if (rules && rules.includes('CODE_EXECUTION')) {
|
|
1165
|
+
firewall.applyCodeExecutionRules?.(); // Need to add this method
|
|
1166
|
+
}
|
|
1167
|
+
if (rules && rules.includes('TRANSACTION_CONTROL')) {
|
|
1168
|
+
firewall.applyTransactionRules?.(); // Need to add this method
|
|
1169
|
+
}
|
|
1170
|
+
if (rules && rules.includes('REMOTE_OPERATIONS')) {
|
|
1171
|
+
firewall.applyRemoteOperationRules?.(); // Need to add this method
|
|
1172
|
+
}
|
|
1173
|
+
// Apply custom rules
|
|
1174
|
+
const customRules = model[FIREWALL_RULES_KEY];
|
|
1175
|
+
if (Array.isArray(customRules)) {
|
|
1176
|
+
for (const rule of customRules) {
|
|
1177
|
+
if (rule.pattern) {
|
|
1178
|
+
firewall.block(rule.pattern, rule.description);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|