@Security takes the namesimport { Security, SecurityFramework, setupSecurity, setupAllSecurity } from 'ts-prorm-orm';
@Security({
frameworks: [SecurityFramework.GDPR, SecurityFramework.HIPAA],
audit: true,
encrypt: ['ssn', 'diagnosis'],
mask: { ssn: 'ssn', email: 'email' },
retention: { years: 7 },
autoClassify: true,
getUserId: () => currentUser()?.id,
getIpAddress: () => currentRequest()?.ip,
})
class Patient extends Model { /* … */ }
setupSecurity(Patient, prorm); // or setupAllSecurity(prorm) once every model is registeredsetupSecurity() wires the hooks that need a model attached to a PrormsetupAllSecurity(prorm) after all models are registered.| At decoration | At setupSecurity() |
|---|---|
classification (classify, autoClassify), lineage, session isolation, WORM, rate-limit registration, cross-border log, the __security_* flags | encryption hooks, masking hooks, the retention policy, the audit trail, immutability and versioning |
interface SecurityOptions {
frameworks?: SecurityFramework[];
audit?: boolean; // ComplianceAuditTrail on every create/update/destroy
encrypt?: string[]; // FieldEncryption on these columns
mask?: Record<string, 'ssn' | 'card-last4' | 'card-first6' | 'email' | 'phone' | 'full'>;
retention?: RetentionDuration; // DataRetentionPolicy.set(Model, { maxAge })
immutable?: boolean; // ImmutableRecord.makeImmutable
versioning?: boolean; // …and enableVersioning
rls?: (ctx: any) => { where?: Record<string, any> }; // RowLevelSecurity.apply
classify?: Record<string, 'PII' | 'PHI' | 'PCI' | 'FINANCIAL' | 'HEALTH' | 'BIOMETRIC'>;
autoClassify?: boolean; // DataClassifier.autoDiscover by column name
lineage?: boolean; // @LineageTrack
sessionIsolation?: boolean; // @TenantIsolation — with tenantIdField (default 'tenantId')
tenantIdField?: string;
worm?: boolean; // @WORM, retention from `retention` or 7 years
rateLimit?: boolean; // @RateLimit registration
breachDetection?: boolean; // flag only — see below
crossBorderLog?: boolean; // CrossBorderLog.enable
sensitiveDataDiscovery?: boolean; // flag only
consentVersioning?: boolean; // flag only
dsar?: boolean; // flag only
pia?: boolean; // flag only
backupVerification?: boolean; // flag only
queryFirewall?: boolean; // flag only
getUserId?: () => string | number | undefined; // for the audit trail
getIpAddress?: () => string | undefined;
}audit,classification (as autoDiscover), lineage, immutable (withsessionIsolation, worm, rateLimit.encryption, masking, retention,rls. A framework cannot know which columns hold the card number; namingencrypt: ['cardNumber'] saysconsent, tls, pseudonymize,breachDetection, backupVerification, queryFirewall (and thecrossBorderLog, dsar, pia, consentVersioning,sensitiveDataDiscovery options). These set a __security_* flag you cansetupSecurity() warns, once per model,onUnwired: 'throw' to'ignore' once you have wired them yourself.securityWiring(Model) returns { wired, flagOnly }; tooling uses it soConsentRecord,TLSEnforcer, PseudonymizationService,BreachDetector, QueryFirewall.import {
Model, Security, SecurityFramework, setupSecurity, securityWiring,
configureFieldEncryption, TLSEnforcer, Prorm,
} from 'ts-prorm-orm';
declare const prorm: Prorm;
configureFieldEncryption(process.env.FIELD_KEY!);
@Security({
frameworks: [SecurityFramework.PCI_DSS],
encrypt: ['cardNumber'],
mask: { cardNumber: 'card-last4' },
onUnwired: 'throw', // refuse to boot with a control that nothing enforces
})
class PaymentMethod extends Model {
declare cardNumber: string;
}
TLSEnforcer.enforce(prorm, { mode: 'strict' }); // the part PCI needs that no decorator can do
try {
setupSecurity(PaymentMethod, prorm);
} catch (err) {
// onUnwired: 'throw' names each gap; here, `tls (implied by a framework)`
console.error((err as Error).message);
}
securityWiring(PaymentMethod);
// { wired: ['encryption', 'masking', 'audit'], flagOnly: ['tls'], unwired: [ … ] }securityWiring() reads what the decorator recorded, so tls stays inflagOnly even after TLSEnforcer.enforce(): the enforcer is attached toProrm instance, not to the model. Once it runs at startup, setonUnwired: 'ignore' on that model. With the default 'warn', the same gap[Security] PaymentMethod: 1 control(s) are recorded but not wired, so nothing enforces them:
- tls (implied by a framework): nothing is wired; call TLSEnforcer.enforce(prorm) at startup and TLSEnforcer.verifyRuntime(prorm)
Set onUnwired: 'ignore' to silence this once you have wired them yourself.[!WARNING]
Naming a framework without the field lists encrypts nothing.frameworks: [SecurityFramework.PCI_DSS]alone recordsencryptionandmaskingas unwired, and the card number is stored in plain text.
SecurityFramework names them; FrameworkCategories groups them;FrameworkRequirements is the table below; getAllFrameworks(),isValidFramework() and getFrameworkCategory() answer questions about them.| Framework | Switches on |
|---|---|
GDPR | audit, encryption, retention, masking, consent, RLS, pseudonymise, classification, TLS, lineage |
CCPA | audit, encryption, retention, masking, consent, RLS |
CPRA | audit, encryption, retention, masking, consent, RLS |
PIPEDA | audit, encryption, retention, consent, RLS |
LGPD | audit, encryption, retention, consent, RLS |
APPI | audit, encryption, consent, RLS |
PIPA | audit, encryption, consent, RLS |
PIPL | audit, encryption, retention, classification, TLS |
DPDP | audit, encryption, retention, consent, RLS |
PDPA_Thailand | audit, encryption, consent, RLS |
PDPA_Singapore | audit, encryption, consent, RLS |
PDPA_Philippines | audit, encryption, consent, RLS |
PDPA_Malaysia | audit, encryption, consent, RLS |
PDPA_Indonesia | audit, encryption, consent, RLS |
PDPA_Vietnam | audit, encryption, consent, RLS |
PoPI | audit, encryption, consent, RLS |
NDPR | audit, encryption, consent, RLS |
KVKK | audit, encryption, consent, RLS |
UK_GDPR | audit, encryption, retention, masking, consent, RLS, pseudonymise, classification, TLS |
| Framework | Switches on |
|---|---|
PCI_DSS | audit, encryption, masking, TLS |
SOX | audit, immutable, retention, lineage, WORM |
GLBA | audit, encryption, masking |
MiFID_II | audit, retention |
EMIR | audit, retention |
Basel_III | audit, retention, lineage |
Dodd_Frank | audit, retention |
SEC_17a_4 | audit, immutable, retention, WORM |
FINRA | audit, retention |
NYDFS | audit, encryption, TLS |
DORA | audit, encryption, retention, RLS, WORM |
| Framework | Switches on |
|---|---|
HIPAA | audit, encryption, masking, RLS, classification, lineage |
HITRUST | audit, encryption, masking, RLS, classification |
21_CFR_Part_11 | audit, immutable |
EU_Annex_11 | audit, immutable |
ALCOA+ | audit, immutable |
GAMP_5 | audit, immutable |
ICH_E6_GCP | audit, immutable |
DISHA | encryption, classification |
| Framework | Switches on |
|---|---|
FedRAMP | encryption, TLS, classification, session isolation |
FISMA | audit, encryption, TLS |
NIST_SP_800_53 | audit, encryption, RLS, classification |
NIST_SP_800_171 | audit, encryption, RLS |
NIST_CSF | audit, encryption, retention, classification, breach detection |
CMMC | encryption, RLS |
DFARS | encryption, RLS |
ITAR | encryption, classification |
CJIS | audit, encryption, RLS |
NERC_CIP | audit, encryption, RLS |
| Framework | Switches on |
|---|---|
ISO_27001 | audit, encryption, RLS, classification |
ISO_27017 | encryption, TLS, session isolation |
ISO_27018 | encryption, classification |
ISO_27701 | classification, consent |
ISO_22301 | retention |
SOC_2 | audit, encryption, retention |
SOC_1 | audit, retention |
CIS_Controls | audit, encryption, breach detection, rate limit, backup verification |
OWASP_Top_10 | encryption, masking, rate limit, query firewall |
| Framework | Switches on |
|---|---|
CLOUD_Act | encryption, classification |
Schrems_II | encryption, pseudonymise |
Essential_Eight | encryption, RLS |
IT_Grundschutz | audit, encryption, RLS |
@Table and @Databaseimport { TableSecurity, DatabaseSecurity } from 'ts-prorm-orm';
@DatabaseSecurity({
name: 'clinical',
region: 'eu-west-1',
requireTLS: true,
defaultClassification: 'CONFIDENTIAL',
defaultAudit: true,
defaultRetention: { years: 7 },
allowedCountries: ['DE', 'FR'],
})
class ClinicalDatabase {}
@TableSecurity({ schema: 'phi', classification: 'SECRET', rlsEnabled: true, auditEnabled: true, dependsOn: ['patients'] })
@Security({ frameworks: [SecurityFramework.HIPAA] })
class Encounter extends Model { /* … */ }Two name clashes to know about. At the package root, Tableis the
model decorator (tableName,timestamps, …) andDatabaseis the
collation decorator. The compliance pair are exported from the root asTableSecurityandDatabaseSecurity, and under their own names
from'ts-prorm-orm/compliance'.
import {
getSecurityFrameworks, getTableSecurity, getDatabaseSecurity, hasAudit, isImmutableModel,
} from 'ts-prorm-orm';
getSecurityFrameworks(Patient); // ['GDPR', 'HIPAA']
hasAudit(Patient); // true
isImmutableModel(LedgerEntry); // true
getTableSecurity(Encounter); // { schema: 'phi', classification: 'SECRET', … }
getDatabaseSecurity(ClinicalDatabase);