@typerion/cli 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/index.cjs +3 -2074
  2. package/package.json +1 -1
package/index.cjs CHANGED
@@ -5848,1794 +5848,6 @@ var require_checker = __commonJS({
5848
5848
  }
5849
5849
  });
5850
5850
 
5851
- // ../typechecker/dist/error-codes.js
5852
- var require_error_codes = __commonJS({
5853
- "../typechecker/dist/error-codes.js"(exports2) {
5854
- "use strict";
5855
- Object.defineProperty(exports2, "__esModule", { value: true });
5856
- exports2.ERROR_CODES = void 0;
5857
- exports2.getErrorCode = getErrorCode;
5858
- exports2.explainErrorCode = explainErrorCode;
5859
- exports2.listErrorCodes = listErrorCodes;
5860
- exports2.ERROR_CODES = {
5861
- // ─── E1xx: Structural Errors ──────────────────────────────────
5862
- E100: {
5863
- code: "E100",
5864
- severity: "error",
5865
- title: "Circular entity dependency",
5866
- description: "Two or more entities reference each other in a cycle via direct field types (not relations). This causes infinite recursion in ORM hydration and JSON serialization.",
5867
- howToFix: "Make one side of the relationship optional, use a reference ID instead, or convert to a RelationType.",
5868
- overridable: false
5869
- },
5870
- // ─── E2xx: Model Completeness Errors ──────────────────────────
5871
- E200: {
5872
- code: "E200",
5873
- severity: "error",
5874
- title: "PII field without encryption",
5875
- description: "A field marked @pii has no @encrypt decorator. GDPR Art.32 and most data protection regulations require encryption of personal data at rest.",
5876
- howToFix: 'Add @encrypt to the field, or explicitly override with @override(rule: "E200", justification: "...").',
5877
- overridable: true
5878
- },
5879
- E201: {
5880
- code: "E201",
5881
- severity: "error",
5882
- title: "Entity exposed via API without policy",
5883
- description: "An entity is referenced by one or more API declarations but has no corresponding policy declaration. Without access control, all fields are accessible to all callers.",
5884
- howToFix: "Add a policy declaration for this entity, e.g.: policy EntityName { read: admin | user }.",
5885
- overridable: true
5886
- },
5887
- E202: {
5888
- code: "E202",
5889
- severity: "error",
5890
- title: "Compliance decorator without policy",
5891
- description: "An entity has a compliance decorator (@gdpr, @hipaa, @soc2, @pci) but no policy declaration. Compliance frameworks require explicit access control definitions.",
5892
- howToFix: "Add a policy declaration for this entity.",
5893
- overridable: true
5894
- },
5895
- E203: {
5896
- code: "E203",
5897
- severity: "error",
5898
- title: "Sensitive field exposed in API response",
5899
- description: "A field marked @sensitive, @secret, or @private is part of an entity returned by an API that has no field-level policy restricting access to this field.",
5900
- howToFix: "Add a field-level policy to restrict access, or add @mask/@private to exclude from API responses.",
5901
- overridable: true
5902
- },
5903
- E204: {
5904
- code: "E204",
5905
- severity: "error",
5906
- title: "Expired override",
5907
- description: 'An @override decorator has an "until" date that has passed. The suppressed rule is now active again.',
5908
- howToFix: "Remove the @override decorator and fix the underlying issue, or extend the deadline.",
5909
- overridable: false
5910
- },
5911
- E205: {
5912
- code: "E205",
5913
- severity: "error",
5914
- title: "Entity with API lacks runtime enforcement binding",
5915
- description: "An entity is exposed via API and has a policy, but will not generate a runtime enforcement binding (EntityOps). Without EntityOps, policy checks and data protection can be bypassed at runtime.",
5916
- howToFix: "Ensure the entity has both a policy declaration and protected fields, or add @enforce decorator.",
5917
- overridable: true
5918
- },
5919
- E206: {
5920
- code: "E206",
5921
- severity: "error",
5922
- title: "Entity with PII/encrypted fields returns unsealed data",
5923
- description: "An entity has fields decorated with @pii or @encrypt but its EntityOps is configured with sealed: false. Without sealing, returned objects are plain JS \u2014 direct property access bypasses masking and encryption at runtime.",
5924
- howToFix: "Remove 'sealed: false' from the EntityOps config, or ensure all access goes through the enforcement API.",
5925
- overridable: true
5926
- },
5927
- E207: {
5928
- code: "E207",
5929
- severity: "error",
5930
- title: "Federated service has schema hash mismatch",
5931
- description: "A downstream service received an enforcement context token whose schema hash does not match the local schema. This means the services may have incompatible entity definitions, risking field-level enforcement bypass.",
5932
- howToFix: "Redeploy services with a consistent schema, or set onSchemaMismatch to 'warn' during rolling deployments.",
5933
- overridable: true
5934
- },
5935
- // ─── E3xx: Decorator Errors ───────────────────────────────────
5936
- E300: {
5937
- code: "E300",
5938
- severity: "error",
5939
- title: "Unknown decorator (strict mode)",
5940
- description: "A decorator was used that is not in the known decorator registry. In strict mode, all decorators must be recognized.",
5941
- howToFix: "Check the decorator name for typos, or register a custom decorator via the plugin system.",
5942
- overridable: true
5943
- },
5944
- E301: {
5945
- code: "E301",
5946
- severity: "error",
5947
- title: "Decorator type mismatch",
5948
- description: "A decorator was applied to a field type that it does not support. For example, @currency requires a Money type, @precision requires a numeric type.",
5949
- howToFix: "Change the field type to match the decorator's requirements.",
5950
- overridable: false
5951
- },
5952
- // ─── E4xx: Policy / Security Errors ───────────────────────────
5953
- E400: {
5954
- code: "E400",
5955
- severity: "error",
5956
- title: "Auth decorator without policy",
5957
- description: "An API has an @auth decorator but the entity it returns has no policy declaration. This creates a false sense of security \u2014 auth is checked but no field-level access control exists.",
5958
- howToFix: "Add a policy declaration for the returned entity.",
5959
- overridable: true
5960
- },
5961
- // ─── E7xx: Test Quality Errors ─────────────────────────────────
5962
- E700: {
5963
- code: "E700",
5964
- severity: "error",
5965
- title: "'any' type in test file",
5966
- description: "A test file (.test.typerion or .spec.typerion) uses the 'any' type. Tests should be at least as type-strict as production code \u2014 untyped test data silently drifts when entities evolve.",
5967
- howToFix: "Replace 'any' with the concrete entity type or use a typed test factory (e.g. buildUser()).",
5968
- overridable: true
5969
- },
5970
- // ─── E8xx: Connector Binding Errors ────────────────────────────
5971
- E800: {
5972
- code: "E800",
5973
- severity: "error",
5974
- title: "Unknown domain in @use",
5975
- description: "The @use decorator references a domain name that is not in the standard registry. Valid domains are: payments, identity, communication, storage, ai.",
5976
- howToFix: 'Use a known domain name: @use("payments"), @use("identity"), @use("communication"), @use("storage"), or @use("ai").',
5977
- overridable: false
5978
- },
5979
- E801: {
5980
- code: "E801",
5981
- severity: "error",
5982
- title: "@use without provider binding",
5983
- description: "An entity uses @use to declare a domain dependency, but no provider is configured for that domain in typerion.config.json. Without a provider binding, codegen cannot generate the adapter service.",
5984
- howToFix: 'Add a connector binding in typerion.config.json: { "connectors": { "payments": { "provider": "stripe" } } }, or run: typerion add stripe',
5985
- overridable: false
5986
- },
5987
- // ─── W2xx: Cross-target Compatibility Warnings ────────────────
5988
- W201: {
5989
- code: "W201",
5990
- severity: "warning",
5991
- title: "Nested arrays (SQL-unfriendly)",
5992
- description: "SQL databases have no native nested array support; this will be serialized as JSON. ORMs may not be able to query into nested array elements.",
5993
- howToFix: "Consider flattening to a separate entity with a foreign key.",
5994
- overridable: true
5995
- },
5996
- W202: {
5997
- code: "W202",
5998
- severity: "warning",
5999
- title: "Non-literal union type",
6000
- description: "SQL maps this to TEXT/JSON (no type safety). Go maps to interface{} (no type safety). Protobuf has no union support (maps to bytes).",
6001
- howToFix: "Consider using an enum or discriminated type.",
6002
- overridable: true
6003
- },
6004
- W203: {
6005
- code: "W203",
6006
- severity: "warning",
6007
- title: "Intersection type (cross-target risk)",
6008
- description: "Most target languages (Go, Rust, SQL, Protobuf) cannot represent intersections natively. This will fall back to a generic/opaque type.",
6009
- howToFix: "Consider merging into a single entity or type.",
6010
- overridable: true
6011
- },
6012
- W204: {
6013
- code: "W204",
6014
- severity: "warning",
6015
- title: "Tuple type (limited support)",
6016
- description: "SQL, Go, and Protobuf have no native tuple support. This will be serialized as JSON array in SQL, fixed-size array in Go.",
6017
- howToFix: "Consider using a named entity with explicit fields.",
6018
- overridable: true
6019
- },
6020
- W205: {
6021
- code: "W205",
6022
- severity: "warning",
6023
- title: "Dynamic type (safety bypass)",
6024
- description: "This bypasses type safety in all targets (any/interface{}/serde_json::Value). ORM validation and Protobuf schema generation will be ineffective.",
6025
- howToFix: "Consider using a more specific type or union.",
6026
- overridable: true
6027
- },
6028
- W206: {
6029
- code: "W206",
6030
- severity: "warning",
6031
- title: "'any' type usage",
6032
- description: "This disables type checking across all targets. SQL maps to JSON/JSONB, Protobuf to Any.",
6033
- howToFix: "Consider using a more specific type or union.",
6034
- overridable: true
6035
- },
6036
- // ─── E9xx: Connector Compliance Errors ──────────────────────────
6037
- E900: {
6038
- code: "E900",
6039
- severity: "error",
6040
- title: "Payment webhook missing replay protection",
6041
- description: 'An entity using @use("payments") has a webhook handler but no replay protection (idempotency key or event deduplication). Replayed webhooks can cause double charges.',
6042
- howToFix: "Add webhook signature verification and event ID deduplication to your payment webhook handler.",
6043
- overridable: true
6044
- },
6045
- E901: {
6046
- code: "E901",
6047
- severity: "error",
6048
- title: "Storage provider missing tenant isolation",
6049
- description: `An entity using @use("storage") does not scope object keys by tenant. Without tenant isolation, one tenant could access another's files.`,
6050
- howToFix: "Ensure all storage keys are prefixed with tenantId (e.g. `${tenantId}/${key}`).",
6051
- overridable: true
6052
- },
6053
- E902: {
6054
- code: "E902",
6055
- severity: "error",
6056
- title: "Identity provider missing session rotation",
6057
- description: 'An entity using @use("identity") does not implement session revocation. Without session rotation, compromised sessions remain valid indefinitely.',
6058
- howToFix: "Implement revokeSession() in your identity connector and expose a session management endpoint.",
6059
- overridable: true
6060
- },
6061
- // ─── W3xx: Model Completeness Warnings (default mode) ────────
6062
- W300: {
6063
- code: "W300",
6064
- severity: "warning",
6065
- title: "PII field without encryption",
6066
- description: "Same as E200 but in default (non-strict) mode.",
6067
- howToFix: "Add @encrypt to the field, or enable strict mode for full enforcement.",
6068
- overridable: true
6069
- },
6070
- W301: {
6071
- code: "W301",
6072
- severity: "warning",
6073
- title: "Entity exposed via API without policy",
6074
- description: "Same as E201 but in default (non-strict) mode.",
6075
- howToFix: "Add a policy declaration for this entity.",
6076
- overridable: true
6077
- },
6078
- W302: {
6079
- code: "W302",
6080
- severity: "warning",
6081
- title: "Compliance decorator without policy",
6082
- description: "Same as E202 but in default (non-strict) mode.",
6083
- howToFix: "Add a policy declaration for this entity.",
6084
- overridable: true
6085
- },
6086
- W303: {
6087
- code: "W303",
6088
- severity: "warning",
6089
- title: "Sensitive field exposed in API response",
6090
- description: "Same as E203 but in default (non-strict) mode.",
6091
- howToFix: "Add a field-level policy to restrict access.",
6092
- overridable: true
6093
- },
6094
- W304: {
6095
- code: "W304",
6096
- severity: "warning",
6097
- title: "Entity with API lacks runtime enforcement binding",
6098
- description: "Same as E205 but in default (non-strict) mode.",
6099
- howToFix: "Add a policy declaration for this entity to enable runtime enforcement.",
6100
- overridable: true
6101
- },
6102
- W305: {
6103
- code: "W305",
6104
- severity: "warning",
6105
- title: "Entity with PII/encrypted fields returns unsealed data",
6106
- description: "Same as E206 but in default (non-strict) mode.",
6107
- howToFix: "Remove 'sealed: false' from the EntityOps config to enable runtime property-level enforcement.",
6108
- overridable: true
6109
- },
6110
- W306: {
6111
- code: "W306",
6112
- severity: "warning",
6113
- title: "Federated service has schema hash mismatch",
6114
- description: "Same as E207 but in default (non-strict) mode. Schema hash mismatch detected during context verification.",
6115
- howToFix: "Redeploy services with a consistent schema, or set onSchemaMismatch to 'ignore' if intentional.",
6116
- overridable: true
6117
- },
6118
- // ─── E6xx: Behavior / Workflow Errors ─────────────────────────
6119
- E600: {
6120
- code: "E600",
6121
- severity: "error",
6122
- title: "Workflow unreachable state",
6123
- description: "A state has no incoming transition and is not marked as initial. It can never be entered.",
6124
- howToFix: "Add a transition leading to this state, mark it @initial, or remove it.",
6125
- overridable: true
6126
- },
6127
- E601: {
6128
- code: "E601",
6129
- severity: "error",
6130
- title: "Workflow dead-end state",
6131
- description: "A non-terminal state has no outgoing transition. The workflow will be stuck if it enters this state.",
6132
- howToFix: "Add an outgoing transition from this state or mark it @terminal.",
6133
- overridable: true
6134
- },
6135
- E602: {
6136
- code: "E602",
6137
- severity: "error",
6138
- title: "Workflow invalid transition reference",
6139
- description: "A transition references a state name that is not defined in the workflow.",
6140
- howToFix: "Define the missing state with 'state StateName' or correct the transition.",
6141
- overridable: false
6142
- },
6143
- E603: {
6144
- code: "E603",
6145
- severity: "error",
6146
- title: "Workflow missing initial state",
6147
- description: "No state in the workflow is marked as initial and could not be inferred.",
6148
- howToFix: "Mark one state with @initial or ensure at least one state is defined.",
6149
- overridable: false
6150
- },
6151
- E604: {
6152
- code: "E604",
6153
- severity: "error",
6154
- title: "Rule references undefined entity or field",
6155
- description: "A rule's 'when' or 'then' clause references an entity or field that doesn't exist in the model.",
6156
- howToFix: "Check the entity/field names in the rule expression and correct any typos.",
6157
- overridable: true
6158
- },
6159
- E605: {
6160
- code: "E605",
6161
- severity: "error",
6162
- title: "Invariant expression type error",
6163
- description: "An invariant expression references fields that don't exist on the entity.",
6164
- howToFix: "Check field names in the invariant expression.",
6165
- overridable: true
6166
- },
6167
- E606: {
6168
- code: "E606",
6169
- severity: "error",
6170
- title: "Lineage source references undefined entity or field",
6171
- description: "A lineage declaration's 'source' references an entity.field path that doesn't exist.",
6172
- howToFix: "Correct the source path to reference an existing entity and field.",
6173
- overridable: true
6174
- },
6175
- E607: {
6176
- code: "E607",
6177
- severity: "error",
6178
- title: "Workflow duplicate state name",
6179
- description: "Two or more states in the same workflow have the same name.",
6180
- howToFix: "Rename one of the duplicate states.",
6181
- overridable: false
6182
- },
6183
- // ─── W6xx: Behavior Warnings (warn mode) ─────────────────────
6184
- W600: {
6185
- code: "W600",
6186
- severity: "warning",
6187
- title: "Workflow unreachable state",
6188
- description: "Same as E600 but in default (non-strict) mode.",
6189
- howToFix: "Add a transition leading to this state, mark it @initial, or remove it.",
6190
- overridable: true
6191
- },
6192
- W601: {
6193
- code: "W601",
6194
- severity: "warning",
6195
- title: "Workflow dead-end state",
6196
- description: "Same as E601 but in default (non-strict) mode.",
6197
- howToFix: "Add an outgoing transition from this state or mark it @terminal.",
6198
- overridable: true
6199
- },
6200
- W604: {
6201
- code: "W604",
6202
- severity: "warning",
6203
- title: "Rule references undefined entity or field",
6204
- description: "Same as E604 but in default (non-strict) mode.",
6205
- howToFix: "Check the entity/field names in the rule expression and correct any typos.",
6206
- overridable: true
6207
- },
6208
- W605: {
6209
- code: "W605",
6210
- severity: "warning",
6211
- title: "Invariant expression type error",
6212
- description: "Same as E605 but in default (non-strict) mode.",
6213
- howToFix: "Check field names in the invariant expression.",
6214
- overridable: true
6215
- },
6216
- W606: {
6217
- code: "W606",
6218
- severity: "warning",
6219
- title: "Lineage source references undefined entity or field",
6220
- description: "Same as E606 but in default (non-strict) mode.",
6221
- howToFix: "Correct the source path to reference an existing entity and field.",
6222
- overridable: true
6223
- },
6224
- // ─── E10xx: System Execution Layer ─────────────────────────────
6225
- E1000: {
6226
- code: "E1000",
6227
- severity: "error",
6228
- title: "System tick exceeds maxLatency",
6229
- description: "The system timing tick interval is greater than the maxLatency constraint. This means the system cannot guarantee real-time deadlines.",
6230
- howToFix: "Set tick <= maxLatency in the timing block.",
6231
- overridable: false
6232
- },
6233
- E1001: {
6234
- code: "E1001",
6235
- severity: "error",
6236
- title: "Sensor emission references undefined event",
6237
- description: "A sensor emits an event type that is not declared as an event in the schema.",
6238
- howToFix: "Declare the event type or correct the sensor emission reference.",
6239
- overridable: true
6240
- },
6241
- E1002: {
6242
- code: "E1002",
6243
- severity: "error",
6244
- title: "Sensor emission interval below system tick",
6245
- description: "A sensor emits events faster than the system tick can process them. Events may be lost or delayed.",
6246
- howToFix: "Increase the sensor interval or decrease the system tick.",
6247
- overridable: true
6248
- },
6249
- E1003: {
6250
- code: "E1003",
6251
- severity: "error",
6252
- title: "Duplicate sensor or actuator name",
6253
- description: "Two sensors or actuators share the same name, creating an ambiguous reference.",
6254
- howToFix: "Rename one of the duplicate declarations.",
6255
- overridable: false
6256
- },
6257
- E1004: {
6258
- code: "E1004",
6259
- severity: "error",
6260
- title: "Actuator has duplicate command name",
6261
- description: "An actuator declares two commands with the same name.",
6262
- howToFix: "Rename one of the duplicate commands.",
6263
- overridable: false
6264
- },
6265
- // ─── W10xx: System Execution Layer (warnings, default mode) ───
6266
- W1000: {
6267
- code: "W1000",
6268
- severity: "warning",
6269
- title: "System tick exceeds maxLatency",
6270
- description: "Same as E1000 but in default (non-strict) mode.",
6271
- howToFix: "Set tick <= maxLatency in the timing block.",
6272
- overridable: true
6273
- },
6274
- W1001: {
6275
- code: "W1001",
6276
- severity: "warning",
6277
- title: "Sensor emission references undefined event",
6278
- description: "Same as E1001 but in default (non-strict) mode.",
6279
- howToFix: "Declare the event type or correct the sensor emission reference.",
6280
- overridable: true
6281
- },
6282
- W1002: {
6283
- code: "W1002",
6284
- severity: "warning",
6285
- title: "Sensor emission interval below system tick",
6286
- description: "Same as E1002 but in default (non-strict) mode.",
6287
- howToFix: "Increase the sensor interval or decrease the system tick.",
6288
- overridable: true
6289
- }
6290
- };
6291
- function getErrorCode(code) {
6292
- return exports2.ERROR_CODES[code];
6293
- }
6294
- function explainErrorCode(code) {
6295
- const entry = exports2.ERROR_CODES[code];
6296
- if (!entry)
6297
- return `Unknown error code: ${code}`;
6298
- return [
6299
- `${entry.code}: ${entry.title}`,
6300
- ` Severity: ${entry.severity}`,
6301
- ` ${entry.description}`,
6302
- ` How to fix: ${entry.howToFix}`,
6303
- entry.overridable ? ' This rule can be overridden with @override(rule: "' + entry.code + '", justification: "...").' : " This rule cannot be overridden."
6304
- ].join("\n");
6305
- }
6306
- function listErrorCodes() {
6307
- return Object.values(exports2.ERROR_CODES).sort((a, b) => a.code.localeCompare(b.code));
6308
- }
6309
- }
6310
- });
6311
-
6312
- // ../typechecker/dist/security-auditor.js
6313
- var require_security_auditor = __commonJS({
6314
- "../typechecker/dist/security-auditor.js"(exports2) {
6315
- "use strict";
6316
- Object.defineProperty(exports2, "__esModule", { value: true });
6317
- exports2.SecurityAuditor = void 0;
6318
- var core_1 = require_dist();
6319
- var SECRET_FIELD_PATTERNS = Object.freeze([
6320
- /password/i,
6321
- /passwd/i,
6322
- /api[_-]?key/i,
6323
- /secret[_-]?key/i,
6324
- /access[_-]?token/i,
6325
- /refresh[_-]?token/i,
6326
- /private[_-]?key/i,
6327
- /session[_-]?token/i,
6328
- /client[_-]?secret/i,
6329
- /webhook[_-]?secret/i,
6330
- /signing[_-]?key/i,
6331
- /jwt[_-]?secret/i
6332
- ]);
6333
- var PII_FIELD_PATTERNS = Object.freeze([
6334
- /^email$/i,
6335
- /^phone(?:[_-]?number)?$/i,
6336
- /^ssn$/i,
6337
- /^(?:national[_-]?)?id[_-]?number$/i,
6338
- /^date[_-]?of[_-]?birth$/i,
6339
- /^(?:street[_-]?)?address$/i,
6340
- /^postal[_-]?code$/i,
6341
- /^credit[_-]?card/i,
6342
- /^iban$/i,
6343
- /^tax[_-]?id$/i,
6344
- /^passport/i
6345
- ]);
6346
- var FINANCIAL_FIELD_PATTERNS = Object.freeze([
6347
- /^balance$/i,
6348
- /^amount$/i,
6349
- /^credit$/i,
6350
- /^debit$/i,
6351
- /^total$/i,
6352
- /^net[_-]?worth$/i
6353
- ]);
6354
- var WRITE_API_PREFIXES = Object.freeze([
6355
- "create",
6356
- "update",
6357
- "delete",
6358
- "remove",
6359
- "set",
6360
- "reset",
6361
- "upsert",
6362
- "patch",
6363
- "cancel",
6364
- "refund",
6365
- "transfer",
6366
- "issue",
6367
- "revoke",
6368
- "grant"
6369
- ]);
6370
- var COMPLIANCE_DECORATORS = /* @__PURE__ */ new Set([
6371
- "gdpr",
6372
- "hipaa",
6373
- "soc2",
6374
- "pci",
6375
- "ccpa",
6376
- "pipeda"
6377
- ]);
6378
- var AUTH_DECORATORS = /* @__PURE__ */ new Set([
6379
- "auth",
6380
- "authenticated",
6381
- "authorize",
6382
- "requireAuth",
6383
- "protected"
6384
- ]);
6385
- var PROTECTION_DECORATORS = /* @__PURE__ */ new Set([
6386
- "secret",
6387
- "encrypt",
6388
- "pii",
6389
- "sensitive",
6390
- "mask"
6391
- ]);
6392
- var PII_DECORATORS = /* @__PURE__ */ new Set(["pii", "sensitive"]);
6393
- var RULES = Object.freeze({
6394
- S100: { errorCode: "S100", warnCode: "S100", title: "secret-named field without @secret/@encrypt" },
6395
- S101: { errorCode: "S101", warnCode: "S101", title: "PII-shaped field without @pii" },
6396
- S102: { errorCode: "S102", warnCode: "S102", title: "write API without @auth" },
6397
- S103: { errorCode: "S103", warnCode: "S103", title: "financial field mutable via API without @immutable or policy" },
6398
- S104: { errorCode: "S104", warnCode: "S104", title: "public read API returns sensitive entity without @auth" },
6399
- S105: { errorCode: "S105", warnCode: "S105", title: "entity references a stricter-tagged entity without reciprocal tag" },
6400
- S106: { errorCode: "S106", warnCode: "S106", title: "sensitive entity uses predictable numeric id" },
6401
- S107: { errorCode: "S107", warnCode: "S107", title: "public read API transitively exposes sensitive data through entity references" },
6402
- S108: { errorCode: "S108", warnCode: "S108", title: "write API transitively reaches a financial field without @immutable" },
6403
- S109: { errorCode: "S109", warnCode: "S109", title: "write API missing @rateLimit / @throttle" },
6404
- S110: { errorCode: "S110", warnCode: "S110", title: "@deprecated API missing @sunset" }
6405
- });
6406
- var RATE_LIMIT_DECORATORS = /* @__PURE__ */ new Set(["rateLimit", "rate_limit", "ratelimit", "throttle"]);
6407
- var SecurityAuditor = class {
6408
- level;
6409
- disabled;
6410
- constructor(level = "warn", options = {}) {
6411
- this.level = level;
6412
- this.disabled = new Set(options.disable ?? []);
6413
- }
6414
- /**
6415
- * Run every rule and return the combined diagnostics.
6416
- */
6417
- check(program) {
6418
- return this.audit(program).findings.slice();
6419
- }
6420
- /**
6421
- * Run every rule and return the full summary (diagnostics + counts).
6422
- * Useful for the CLI to render a per-rule table without re-grouping.
6423
- */
6424
- audit(program) {
6425
- const counts = {
6426
- S100: 0,
6427
- S101: 0,
6428
- S102: 0,
6429
- S103: 0,
6430
- S104: 0,
6431
- S105: 0,
6432
- S106: 0,
6433
- S107: 0,
6434
- S108: 0,
6435
- S109: 0,
6436
- S110: 0
6437
- };
6438
- if (this.level === "off") {
6439
- return { findings: [], ruleCounts: counts };
6440
- }
6441
- const entities = collectEntities(program);
6442
- const apis = collectAPIs(program);
6443
- const policies = collectPolicies(program);
6444
- const overrides = collectOverrides(program);
6445
- const diagnostics = [];
6446
- const sensitiveEntities = findSensitiveEntities(entities);
6447
- const complianceEntities = findComplianceEntities(entities);
6448
- const policiesByEntity = new Map(policies.map((p) => [p.entity, p]));
6449
- for (const entity of entities.values()) {
6450
- const entityOverrides = overrides.filter((o) => o.entityName === entity.name);
6451
- for (const field of entity.fields) {
6452
- this.runFieldRules(entity, field, entityOverrides, diagnostics, counts);
6453
- }
6454
- this.runPredictableIdRule(entity, entityOverrides, sensitiveEntities, diagnostics, counts);
6455
- this.runRelationComplianceRule(entity, entities, complianceEntities, entityOverrides, diagnostics, counts);
6456
- }
6457
- for (const api of apis) {
6458
- this.runWriteApiAuthRule(api, diagnostics, counts);
6459
- this.runPublicReadSensitiveRule(api, entities, sensitiveEntities, diagnostics, counts);
6460
- this.runFinancialMutationRule(api, entities, policiesByEntity, diagnostics, counts);
6461
- this.runRateLimitRule(api, diagnostics, counts);
6462
- this.runDeprecationSunsetRule(api, diagnostics, counts);
6463
- }
6464
- if (!this.disabled.has("S107") || !this.disabled.has("S108")) {
6465
- const graph = buildEntityGraph(entities);
6466
- for (const api of apis) {
6467
- this.runTaintFlowRules(api, entities, graph, policiesByEntity, diagnostics, counts);
6468
- }
6469
- }
6470
- return { findings: diagnostics, ruleCounts: counts };
6471
- }
6472
- // ─── Field-level rules ──────────────────────────────────────────
6473
- runFieldRules(entity, field, entityOverrides, diagnostics, counts) {
6474
- const fieldOverrides = entityOverrides.filter((o) => o.fieldName === field.name || !o.fieldName);
6475
- const decoratorNames = decoratorSet(field.decorators);
6476
- if (!this.disabled.has("S100") && matchesAny(field.name, SECRET_FIELD_PATTERNS)) {
6477
- const hasSecret = decoratorNames.has("secret") || decoratorNames.has("encrypt");
6478
- if (!hasSecret) {
6479
- this.emit("S100", fieldOverrides, diagnostics, counts, field.location, `Field '${entity.name}.${field.name}' looks like a secret but has no @secret or @encrypt decorator.`);
6480
- }
6481
- }
6482
- if (!this.disabled.has("S101") && matchesAny(field.name, PII_FIELD_PATTERNS)) {
6483
- const hasPII = hasAnyOf(decoratorNames, PII_DECORATORS);
6484
- if (!hasPII) {
6485
- this.emit("S101", fieldOverrides, diagnostics, counts, field.location, `Field '${entity.name}.${field.name}' looks like PII but has no @pii decorator.`);
6486
- }
6487
- }
6488
- }
6489
- runPredictableIdRule(entity, entityOverrides, sensitiveEntities, diagnostics, counts) {
6490
- if (this.disabled.has("S106"))
6491
- return;
6492
- if (!sensitiveEntities.has(entity.name))
6493
- return;
6494
- const idField = entity.fields.find((f) => /^id$/i.test(f.name));
6495
- if (!idField)
6496
- return;
6497
- const typeName = typeReferenceName(idField.typeExpr);
6498
- if (typeName === "number" || typeName === "int") {
6499
- this.emit("S106", entityOverrides, diagnostics, counts, idField.location, `Entity '${entity.name}' carries sensitive fields but uses a numeric id \u2014 prefer string/UUID to avoid enumeration attacks.`);
6500
- }
6501
- }
6502
- runRelationComplianceRule(entity, entities, complianceEntities, entityOverrides, diagnostics, counts) {
6503
- if (this.disabled.has("S105"))
6504
- return;
6505
- const selfCompliance = decoratorNameSet(entity.decorators);
6506
- const selfHasCompliance = hasAnyOf(selfCompliance, COMPLIANCE_DECORATORS);
6507
- if (selfHasCompliance)
6508
- return;
6509
- for (const field of entity.fields) {
6510
- const refName = typeReferenceName(field.typeExpr);
6511
- if (!refName)
6512
- continue;
6513
- if (!entities.has(refName))
6514
- continue;
6515
- if (!complianceEntities.has(refName))
6516
- continue;
6517
- this.emit("S105", entityOverrides, diagnostics, counts, field.location, `Entity '${entity.name}' references compliance-tagged '${refName}' via '${field.name}' but carries no reciprocal compliance decorator \u2014 data from '${refName}' may leak through '${entity.name}'.`);
6518
- }
6519
- }
6520
- // ─── API-level rules ────────────────────────────────────────────
6521
- runWriteApiAuthRule(api, diagnostics, counts) {
6522
- if (this.disabled.has("S102"))
6523
- return;
6524
- if (!isWriteApi(api.name))
6525
- return;
6526
- const decs = decoratorNameSet(api.decorators);
6527
- if (hasAnyOf(decs, AUTH_DECORATORS))
6528
- return;
6529
- this.emit("S102", [], diagnostics, counts, api.location, `Write API '${api.name}' has no @auth decorator \u2014 any caller can mutate state.`);
6530
- }
6531
- runPublicReadSensitiveRule(api, entities, sensitiveEntities, diagnostics, counts) {
6532
- if (this.disabled.has("S104"))
6533
- return;
6534
- const decs = decoratorNameSet(api.decorators);
6535
- if (hasAnyOf(decs, AUTH_DECORATORS))
6536
- return;
6537
- const entityNames = collectReferencedEntityNames(api.returnType);
6538
- for (const name of entityNames) {
6539
- if (!sensitiveEntities.has(name))
6540
- continue;
6541
- this.emit("S104", [], diagnostics, counts, api.location, `API '${api.name}' returns '${name}' which carries @pii/@secret fields, but has no @auth decorator \u2014 sensitive data is publicly reachable.`);
6542
- return;
6543
- }
6544
- }
6545
- runFinancialMutationRule(api, entities, policiesByEntity, diagnostics, counts) {
6546
- if (this.disabled.has("S103"))
6547
- return;
6548
- if (!isWriteApi(api.name))
6549
- return;
6550
- const entityNames = collectReferencedEntityNames(api.returnType);
6551
- for (const name of entityNames) {
6552
- const entity = entities.get(name);
6553
- if (!entity)
6554
- continue;
6555
- const financialFields = entity.fields.filter((f) => matchesAny(f.name, FINANCIAL_FIELD_PATTERNS));
6556
- if (financialFields.length === 0)
6557
- continue;
6558
- const policy = policiesByEntity.get(name);
6559
- const protectedFields = new Set(policy?.fieldPolicies.map((fp) => fp.fieldName) ?? []);
6560
- for (const field of financialFields) {
6561
- const decs = decoratorNameSet(field.decorators);
6562
- const immutable = decs.has("immutable") || decs.has("readonly");
6563
- if (immutable)
6564
- continue;
6565
- if (protectedFields.has(field.name))
6566
- continue;
6567
- this.emit("S103", [], diagnostics, counts, api.location, `Write API '${api.name}' can mutate '${name}.${field.name}' (financial) but the field has no @immutable decorator or field-level policy.`);
6568
- }
6569
- }
6570
- }
6571
- runRateLimitRule(api, diagnostics, counts) {
6572
- if (this.disabled.has("S109"))
6573
- return;
6574
- if (!isWriteApi(api.name))
6575
- return;
6576
- const decs = decoratorNameSet(api.decorators);
6577
- if (hasAnyOf(decs, RATE_LIMIT_DECORATORS))
6578
- return;
6579
- this.emit("S109", [], diagnostics, counts, api.location, `Write API '${api.name}' has no @rateLimit / @throttle \u2014 unbounded call rate exposes the endpoint to brute-force or DoS.`);
6580
- }
6581
- runDeprecationSunsetRule(api, diagnostics, counts) {
6582
- if (this.disabled.has("S110"))
6583
- return;
6584
- const decs = decoratorNameSet(api.decorators);
6585
- if (!decs.has("deprecated"))
6586
- return;
6587
- if (decs.has("sunset"))
6588
- return;
6589
- this.emit("S110", [], diagnostics, counts, api.location, `API '${api.name}' is @deprecated but has no @sunset \u2014 no scheduled removal date means the deprecation drifts indefinitely.`);
6590
- }
6591
- // ─── Taint-flow rules (S107 + S108) ─────────────────────────────
6592
- /**
6593
- * Walk the entity-reference graph starting from `api.returnType` and report
6594
- * transitive exposure of sensitive data or financial mutation paths.
6595
- *
6596
- * S107 (read path): an unauthenticated API whose return type transitively
6597
- * reaches a field carrying @pii / @secret / @sensitive.
6598
- *
6599
- * S108 (write path): a write API (create/update/delete/transfer prefixes)
6600
- * whose return type transitively reaches a financial field (`balance`,
6601
- * `amount`, …) that lacks @immutable / @readonly.
6602
- *
6603
- * Depth is capped at 6 hops — that covers the realistic contracts without
6604
- * exploring cycles. The walk uses a visited set to stop them cold.
6605
- */
6606
- runTaintFlowRules(api, entities, graph, policiesByEntity, diagnostics, counts) {
6607
- const decs = decoratorNameSet(api.decorators);
6608
- const isAuthenticated = hasAnyOf(decs, AUTH_DECORATORS);
6609
- const isWrite = isWriteApi(api.name);
6610
- const roots = collectReferencedEntityNames(api.returnType);
6611
- if (roots.size === 0)
6612
- return;
6613
- const MAX_DEPTH = 6;
6614
- for (const root of roots) {
6615
- if (!entities.has(root))
6616
- continue;
6617
- if (!this.disabled.has("S107") && !isAuthenticated) {
6618
- const hit = findFirstSensitiveField(root, entities, graph, MAX_DEPTH);
6619
- if (hit && hit.depth >= 1) {
6620
- const pathStr = renderPath(api.name, hit.path, hit.field.entity, hit.field.name, hit.field.decorator);
6621
- this.emit("S107", [], diagnostics, counts, api.location, `Public API '${api.name}' transitively exposes ${hit.field.decorator === "secret" ? "a secret" : "PII"} at '${hit.field.entity}.${hit.field.name}' \u2014 path: ${pathStr}.`);
6622
- break;
6623
- }
6624
- }
6625
- if (!this.disabled.has("S108") && isWrite) {
6626
- const hit = findFirstFinancialField(root, entities, graph, policiesByEntity, MAX_DEPTH);
6627
- if (hit && hit.depth >= 1) {
6628
- const pathStr = renderPath(api.name, hit.path, hit.field.entity, hit.field.name, "financial");
6629
- this.emit("S108", [], diagnostics, counts, api.location, `Write API '${api.name}' can transitively mutate financial field '${hit.field.entity}.${hit.field.name}' \u2014 path: ${pathStr}. Mark the field @immutable or gate it with a field-level policy.`);
6630
- break;
6631
- }
6632
- }
6633
- }
6634
- }
6635
- // ─── Emit ───────────────────────────────────────────────────────
6636
- emit(code, overrides, diagnostics, counts, location, message) {
6637
- const active = overrides.find((o) => {
6638
- if (o.rule !== code)
6639
- return false;
6640
- if (o.until) {
6641
- const expires = new Date(o.until);
6642
- if (!Number.isNaN(expires.getTime()) && expires < /* @__PURE__ */ new Date())
6643
- return false;
6644
- }
6645
- return true;
6646
- });
6647
- if (active)
6648
- return;
6649
- counts[code]++;
6650
- const severity = this.level === "strict" ? "error" : "warning";
6651
- diagnostics.push((0, core_1.createDiagnostic)(severity, message, location, code));
6652
- }
6653
- };
6654
- exports2.SecurityAuditor = SecurityAuditor;
6655
- function collectEntities(program) {
6656
- const entities = /* @__PURE__ */ new Map();
6657
- for (const decl of program.body) {
6658
- const unwrapped = decl.type === "ExportDeclaration" ? decl.declaration : decl;
6659
- if (unwrapped.type === "EntityDeclaration") {
6660
- entities.set(unwrapped.name, unwrapped);
6661
- }
6662
- }
6663
- return entities;
6664
- }
6665
- function collectAPIs(program) {
6666
- const apis = [];
6667
- for (const decl of program.body) {
6668
- const unwrapped = decl.type === "ExportDeclaration" ? decl.declaration : decl;
6669
- if (unwrapped.type === "APIDeclaration")
6670
- apis.push(unwrapped);
6671
- }
6672
- return apis;
6673
- }
6674
- function collectPolicies(program) {
6675
- const policies = [];
6676
- for (const decl of program.body) {
6677
- const unwrapped = decl.type === "ExportDeclaration" ? decl.declaration : decl;
6678
- if (unwrapped.type === "PolicyDeclaration")
6679
- policies.push(unwrapped);
6680
- }
6681
- return policies;
6682
- }
6683
- function collectOverrides(program) {
6684
- const overrides = [];
6685
- for (const decl of program.body) {
6686
- const unwrapped = decl.type === "ExportDeclaration" ? decl.declaration : decl;
6687
- if (unwrapped.type !== "EntityDeclaration")
6688
- continue;
6689
- collectOverridesFromDecorators(unwrapped.decorators, unwrapped.name, void 0, unwrapped.location, overrides);
6690
- for (const field of unwrapped.fields) {
6691
- collectOverridesFromDecorators(field.decorators, unwrapped.name, field.name, field.location, overrides);
6692
- }
6693
- }
6694
- return overrides;
6695
- }
6696
- function collectOverridesFromDecorators(decorators, entityName, fieldName, location, out) {
6697
- for (const dec of decorators ?? []) {
6698
- if (dec.name !== "override")
6699
- continue;
6700
- if (dec.args.length < 2)
6701
- continue;
6702
- const rule = literalString(dec.args[0]);
6703
- const justification = literalString(dec.args[1]);
6704
- const until = dec.args[2] ? literalString(dec.args[2]) : void 0;
6705
- if (!rule || !justification)
6706
- continue;
6707
- out.push({ rule, justification, until, entityName, fieldName, location });
6708
- }
6709
- }
6710
- function literalString(node) {
6711
- if (!node || typeof node !== "object")
6712
- return void 0;
6713
- const n = node;
6714
- if (n.type !== "LiteralExpression")
6715
- return void 0;
6716
- return typeof n.value === "string" ? n.value : void 0;
6717
- }
6718
- function findSensitiveEntities(entities) {
6719
- const result = /* @__PURE__ */ new Set();
6720
- for (const entity of entities.values()) {
6721
- const entityDecs = decoratorNameSet(entity.decorators);
6722
- if (hasAnyOf(entityDecs, COMPLIANCE_DECORATORS)) {
6723
- result.add(entity.name);
6724
- continue;
6725
- }
6726
- const hasSensitiveField = entity.fields.some((f) => {
6727
- const names = decoratorNameSet(f.decorators);
6728
- return hasAnyOf(names, PROTECTION_DECORATORS);
6729
- });
6730
- if (hasSensitiveField)
6731
- result.add(entity.name);
6732
- }
6733
- return result;
6734
- }
6735
- function findComplianceEntities(entities) {
6736
- const result = /* @__PURE__ */ new Set();
6737
- for (const entity of entities.values()) {
6738
- const decs = decoratorNameSet(entity.decorators);
6739
- if (hasAnyOf(decs, COMPLIANCE_DECORATORS))
6740
- result.add(entity.name);
6741
- }
6742
- return result;
6743
- }
6744
- function decoratorSet(decorators) {
6745
- return decoratorNameSet(decorators);
6746
- }
6747
- function decoratorNameSet(decorators) {
6748
- const out = /* @__PURE__ */ new Set();
6749
- for (const d of decorators ?? [])
6750
- out.add(d.name);
6751
- return out;
6752
- }
6753
- function hasAnyOf(names, required) {
6754
- for (const name of required) {
6755
- if (names.has(name))
6756
- return true;
6757
- }
6758
- return false;
6759
- }
6760
- function matchesAny(value, patterns) {
6761
- for (const p of patterns) {
6762
- if (p.test(value))
6763
- return true;
6764
- }
6765
- return false;
6766
- }
6767
- function isWriteApi(name) {
6768
- const lower = name.toLowerCase();
6769
- return WRITE_API_PREFIXES.some((p) => lower.startsWith(p));
6770
- }
6771
- function typeReferenceName(expr) {
6772
- if (expr.type === "TypeReference")
6773
- return expr.name;
6774
- if (expr.type === "ArrayType")
6775
- return typeReferenceName(expr.elementType);
6776
- return void 0;
6777
- }
6778
- function collectReferencedEntityNames(expr) {
6779
- const out = /* @__PURE__ */ new Set();
6780
- visitType(expr, (t) => {
6781
- if (t.type === "TypeReference")
6782
- out.add(t.name);
6783
- });
6784
- return out;
6785
- }
6786
- function visitType(expr, fn) {
6787
- fn(expr);
6788
- if (expr.type === "ArrayType")
6789
- visitType(expr.elementType, fn);
6790
- if (expr.type === "UnionType" && "types" in expr) {
6791
- for (const t of expr.types)
6792
- visitType(t, fn);
6793
- }
6794
- }
6795
- function buildEntityGraph(entities) {
6796
- const graph = /* @__PURE__ */ new Map();
6797
- for (const [name, entity] of entities) {
6798
- const edges = [];
6799
- for (const field of entity.fields) {
6800
- const refs = collectReferencedEntityNames(field.typeExpr);
6801
- for (const target of refs) {
6802
- if (!entities.has(target))
6803
- continue;
6804
- if (target === name)
6805
- continue;
6806
- edges.push({ to: target, via: field.name });
6807
- }
6808
- }
6809
- graph.set(name, edges);
6810
- }
6811
- return graph;
6812
- }
6813
- function findFirstSensitiveField(root, entities, graph, maxDepth) {
6814
- const queue = [{ entity: root, path: [], depth: 0 }];
6815
- const visited = /* @__PURE__ */ new Set([root]);
6816
- while (queue.length > 0) {
6817
- const { entity, path: path2, depth } = queue.shift();
6818
- const decl = entities.get(entity);
6819
- if (!decl)
6820
- continue;
6821
- if (depth >= 1) {
6822
- for (const field of decl.fields) {
6823
- const decs = decoratorNameSet(field.decorators);
6824
- for (const d of ["secret", "encrypt", "pii", "sensitive"]) {
6825
- if (decs.has(d)) {
6826
- return { depth, path: path2, field: { entity, name: field.name, decorator: d } };
6827
- }
6828
- }
6829
- }
6830
- }
6831
- if (depth >= maxDepth)
6832
- continue;
6833
- const edges = graph.get(entity) ?? [];
6834
- for (const edge of edges) {
6835
- if (visited.has(edge.to))
6836
- continue;
6837
- visited.add(edge.to);
6838
- queue.push({ entity: edge.to, path: [...path2, edge], depth: depth + 1 });
6839
- }
6840
- }
6841
- return null;
6842
- }
6843
- function findFirstFinancialField(root, entities, graph, policiesByEntity, maxDepth) {
6844
- const queue = [{ entity: root, path: [], depth: 0 }];
6845
- const visited = /* @__PURE__ */ new Set([root]);
6846
- while (queue.length > 0) {
6847
- const { entity, path: path2, depth } = queue.shift();
6848
- const decl = entities.get(entity);
6849
- if (!decl)
6850
- continue;
6851
- if (depth >= 1) {
6852
- const policy = policiesByEntity.get(entity);
6853
- const guarded = new Set(policy?.fieldPolicies.map((fp) => fp.fieldName) ?? []);
6854
- for (const field of decl.fields) {
6855
- if (!matchesAny(field.name, FINANCIAL_FIELD_PATTERNS))
6856
- continue;
6857
- const decs = decoratorNameSet(field.decorators);
6858
- if (decs.has("immutable") || decs.has("readonly"))
6859
- continue;
6860
- if (guarded.has(field.name))
6861
- continue;
6862
- return { depth, path: path2, field: { entity, name: field.name, decorator: "financial" } };
6863
- }
6864
- }
6865
- if (depth >= maxDepth)
6866
- continue;
6867
- const edges = graph.get(entity) ?? [];
6868
- for (const edge of edges) {
6869
- if (visited.has(edge.to))
6870
- continue;
6871
- visited.add(edge.to);
6872
- queue.push({ entity: edge.to, path: [...path2, edge], depth: depth + 1 });
6873
- }
6874
- }
6875
- return null;
6876
- }
6877
- function renderPath(apiName, path2, leakEntity, leakField, leakKind) {
6878
- const segments = [apiName];
6879
- for (const edge of path2) {
6880
- segments.push(`${edge.to} (via .${edge.via})`);
6881
- }
6882
- segments.push(`${leakEntity}.${leakField} [${leakKind}]`);
6883
- return segments.join(" \u2192 ");
6884
- }
6885
- }
6886
- });
6887
-
6888
- // ../typechecker/dist/security-auditor-sarif.js
6889
- var require_security_auditor_sarif = __commonJS({
6890
- "../typechecker/dist/security-auditor-sarif.js"(exports2) {
6891
- "use strict";
6892
- Object.defineProperty(exports2, "__esModule", { value: true });
6893
- exports2.RULE_CATALOGUE = exports2.TYPERION_AUDITOR_HELP_URI_BASE = exports2.TYPERION_AUDITOR_INFO_URI = exports2.TYPERION_AUDITOR_NAME = exports2.SARIF_VERSION = exports2.SARIF_SCHEMA_URI = void 0;
6894
- exports2.renderSarif = renderSarif;
6895
- exports2.SARIF_SCHEMA_URI = "https://json.schemastore.org/sarif-2.1.0.json";
6896
- exports2.SARIF_VERSION = "2.1.0";
6897
- exports2.TYPERION_AUDITOR_NAME = "Typerion Security Auditor";
6898
- exports2.TYPERION_AUDITOR_INFO_URI = "https://typerion.io";
6899
- exports2.TYPERION_AUDITOR_HELP_URI_BASE = "https://github.com/wiaahmarketplace/Typerion_V1/blob/main/docs/security";
6900
- exports2.RULE_CATALOGUE = Object.freeze({
6901
- S100: {
6902
- shortDescription: "Secret-shaped field is missing @secret or @encrypt.",
6903
- fullDescription: "Fields whose names strongly imply a secret (password, apiKey, refreshToken, etc.) must carry either @secret or @encrypt so the runtime masks and protects the value.",
6904
- defaultLevel: "error"
6905
- },
6906
- S101: {
6907
- shortDescription: "PII-shaped field is missing @pii.",
6908
- fullDescription: "Fields matching canonical PII names (email, ssn, dateOfBirth, iban, etc.) must carry @pii so downstream codegens register them in the data-protection catalogue.",
6909
- defaultLevel: "warning"
6910
- },
6911
- S102: {
6912
- shortDescription: "Write API has no @auth decorator.",
6913
- fullDescription: "Mutations (create/update/delete/transfer/refund/grant prefixes) must gate access with @auth; without it, any caller can change state.",
6914
- defaultLevel: "error"
6915
- },
6916
- S103: {
6917
- shortDescription: "Financial field is mutable via API without @immutable or a field-level policy.",
6918
- fullDescription: "Balance/amount/credit/debit fields on an entity reachable from a write API must be @immutable, @readonly, or covered by a field-level policy to avoid unauthorised balance changes.",
6919
- defaultLevel: "error"
6920
- },
6921
- S104: {
6922
- shortDescription: "Public read API returns a sensitive entity without @auth.",
6923
- fullDescription: "A read API whose return entity carries @pii or @secret fields must gate access with @auth to prevent unauthenticated disclosure.",
6924
- defaultLevel: "error"
6925
- },
6926
- S105: {
6927
- shortDescription: "Entity references a compliance-tagged entity without a reciprocal tag.",
6928
- fullDescription: "When entity A references an entity carrying @gdpr/@hipaa/@pci/@soc2, A should carry a compatible tag so compliance rules propagate consistently.",
6929
- defaultLevel: "warning"
6930
- },
6931
- S106: {
6932
- shortDescription: "Sensitive entity uses a predictable numeric id.",
6933
- fullDescription: "Entities carrying compliance decorators or @pii fields should use an opaque identifier (string/UUID) rather than an incrementing integer to resist enumeration.",
6934
- defaultLevel: "warning"
6935
- },
6936
- S107: {
6937
- shortDescription: "Public API transitively exposes sensitive data through entity references.",
6938
- fullDescription: "Data-flow walk found an unauthenticated API whose return type can reach a field carrying @pii or @secret via one or more reference hops. Gate the API with @auth or mask the leaking field.",
6939
- defaultLevel: "error"
6940
- },
6941
- S108: {
6942
- shortDescription: "Write API transitively reaches a financial field without @immutable.",
6943
- fullDescription: "Data-flow walk found a mutation API that can reach a financial field through one or more reference hops. Mark the field @immutable/@readonly or add a field-level policy to prevent unauthorised mutation.",
6944
- defaultLevel: "error"
6945
- },
6946
- S109: {
6947
- shortDescription: "Write API has no @rateLimit or @throttle decorator.",
6948
- fullDescription: "Mutations (create/update/delete/transfer/refund/grant prefixes) without an explicit rate limit expose the endpoint to brute-force, credential-stuffing, and DoS. Declare @rateLimit(requestsPerWindow) or @throttle(minIntervalMs) on the API, or install a gateway-level bound that you document via @override.",
6949
- defaultLevel: "warning"
6950
- },
6951
- S110: {
6952
- shortDescription: "API marked @deprecated has no @sunset date.",
6953
- fullDescription: 'A @deprecated API without a @sunset removal date drifts forever: consumers keep calling it, and the codebase accumulates obsolete surface area. Add @sunset("YYYY-MM-DD") so the removal date is tracked and auditable.',
6954
- defaultLevel: "warning"
6955
- }
6956
- });
6957
- function renderSarif(findings, options = {}) {
6958
- const version = options.version ?? "0.1.0";
6959
- const informationUri = options.informationUri ?? exports2.TYPERION_AUDITOR_INFO_URI;
6960
- const helpUriBase = options.helpUriBase ?? exports2.TYPERION_AUDITOR_HELP_URI_BASE;
6961
- const workspaceRoot = options.workspaceRoot ?? "";
6962
- const ruleIds = /* @__PURE__ */ new Set();
6963
- for (const d of findings) {
6964
- if (isSecurityRuleId(d.code))
6965
- ruleIds.add(d.code);
6966
- }
6967
- const rules = [];
6968
- for (const id of sortRuleIds([...ruleIds])) {
6969
- const doc = exports2.RULE_CATALOGUE[id];
6970
- rules.push({
6971
- id,
6972
- name: id,
6973
- shortDescription: { text: doc.shortDescription },
6974
- fullDescription: { text: doc.fullDescription },
6975
- helpUri: `${helpUriBase}/${id}.md`,
6976
- defaultConfiguration: { level: doc.defaultLevel }
6977
- });
6978
- }
6979
- const results = [];
6980
- for (const d of findings) {
6981
- if (!isSecurityRuleId(d.code))
6982
- continue;
6983
- const uri = relativiseUri(d.location?.file ?? "", workspaceRoot);
6984
- results.push({
6985
- ruleId: d.code,
6986
- level: d.severity === "error" ? "error" : d.severity === "warning" ? "warning" : "note",
6987
- message: { text: d.message },
6988
- locations: [
6989
- {
6990
- physicalLocation: {
6991
- artifactLocation: { uri },
6992
- region: {
6993
- startLine: d.location?.startLine ?? 1,
6994
- startColumn: d.location?.startColumn,
6995
- endLine: d.location?.endLine,
6996
- endColumn: d.location?.endColumn
6997
- }
6998
- }
6999
- }
7000
- ]
7001
- });
7002
- }
7003
- return {
7004
- $schema: exports2.SARIF_SCHEMA_URI,
7005
- version: exports2.SARIF_VERSION,
7006
- runs: [
7007
- {
7008
- tool: {
7009
- driver: {
7010
- name: exports2.TYPERION_AUDITOR_NAME,
7011
- version,
7012
- informationUri,
7013
- rules
7014
- }
7015
- },
7016
- results
7017
- }
7018
- ]
7019
- };
7020
- }
7021
- function isSecurityRuleId(code) {
7022
- return code !== void 0 && code.length === 4 && code.startsWith("S") && code in exports2.RULE_CATALOGUE;
7023
- }
7024
- function sortRuleIds(ids) {
7025
- return [...ids].sort();
7026
- }
7027
- function relativiseUri(absolutePath, root) {
7028
- if (!absolutePath)
7029
- return "";
7030
- if (!root)
7031
- return absolutePath;
7032
- const normalised = absolutePath.replace(/\\/g, "/");
7033
- const base = root.replace(/\\/g, "/").replace(/\/+$/, "");
7034
- if (normalised.startsWith(base + "/"))
7035
- return normalised.slice(base.length + 1);
7036
- return normalised;
7037
- }
7038
- }
7039
- });
7040
-
7041
- // ../typechecker/dist/security-baseline.js
7042
- var require_security_baseline = __commonJS({
7043
- "../typechecker/dist/security-baseline.js"(exports2) {
7044
- "use strict";
7045
- var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
7046
- if (k2 === void 0) k2 = k;
7047
- var desc = Object.getOwnPropertyDescriptor(m, k);
7048
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7049
- desc = { enumerable: true, get: function() {
7050
- return m[k];
7051
- } };
7052
- }
7053
- Object.defineProperty(o, k2, desc);
7054
- }) : (function(o, m, k, k2) {
7055
- if (k2 === void 0) k2 = k;
7056
- o[k2] = m[k];
7057
- }));
7058
- var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
7059
- Object.defineProperty(o, "default", { enumerable: true, value: v });
7060
- }) : function(o, v) {
7061
- o["default"] = v;
7062
- });
7063
- var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
7064
- var ownKeys = function(o) {
7065
- ownKeys = Object.getOwnPropertyNames || function(o2) {
7066
- var ar = [];
7067
- for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
7068
- return ar;
7069
- };
7070
- return ownKeys(o);
7071
- };
7072
- return function(mod) {
7073
- if (mod && mod.__esModule) return mod;
7074
- var result = {};
7075
- if (mod != null) {
7076
- for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
7077
- }
7078
- __setModuleDefault(result, mod);
7079
- return result;
7080
- };
7081
- })();
7082
- Object.defineProperty(exports2, "__esModule", { value: true });
7083
- exports2.BASELINE_VERSION = void 0;
7084
- exports2.createBaseline = createBaseline;
7085
- exports2.applyBaseline = applyBaseline;
7086
- exports2.serializeBaseline = serializeBaseline;
7087
- exports2.parseBaseline = parseBaseline;
7088
- var path2 = __importStar(require("node:path"));
7089
- exports2.BASELINE_VERSION = 1;
7090
- function normalizeFile(file) {
7091
- if (!file)
7092
- return "";
7093
- const rel = path2.isAbsolute(file) ? path2.relative(process.cwd(), file) : file;
7094
- return rel.split(path2.sep).join("/");
7095
- }
7096
- function createBaseline(findings, clock = () => /* @__PURE__ */ new Date()) {
7097
- const entries = [];
7098
- for (const d of findings) {
7099
- if (!isSecurityCode(d.code))
7100
- continue;
7101
- entries.push({
7102
- ruleId: d.code,
7103
- file: normalizeFile(d.location?.file ?? ""),
7104
- message: d.message
7105
- });
7106
- }
7107
- entries.sort(compareEntries);
7108
- return {
7109
- version: exports2.BASELINE_VERSION,
7110
- createdAt: clock().toISOString(),
7111
- entries
7112
- };
7113
- }
7114
- function applyBaseline(findings, baseline) {
7115
- const keyCounts = /* @__PURE__ */ new Map();
7116
- for (const entry of baseline.entries) {
7117
- const key = entryKey(entry);
7118
- keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
7119
- }
7120
- const remaining = [];
7121
- const suppressed = [];
7122
- for (const d of findings) {
7123
- if (!isSecurityCode(d.code)) {
7124
- remaining.push(d);
7125
- continue;
7126
- }
7127
- const key = findingKey(d);
7128
- const count = keyCounts.get(key) ?? 0;
7129
- if (count > 0) {
7130
- keyCounts.set(key, count - 1);
7131
- suppressed.push(d);
7132
- } else {
7133
- remaining.push(d);
7134
- }
7135
- }
7136
- const unmatched = [];
7137
- for (const entry of baseline.entries) {
7138
- const key = entryKey(entry);
7139
- const remainingQuota = keyCounts.get(key) ?? 0;
7140
- if (remainingQuota > 0) {
7141
- unmatched.push(entry);
7142
- keyCounts.set(key, remainingQuota - 1);
7143
- }
7144
- }
7145
- return { remaining, suppressed, unmatched };
7146
- }
7147
- function serializeBaseline(baseline) {
7148
- return JSON.stringify(baseline, null, 2) + "\n";
7149
- }
7150
- function parseBaseline(text) {
7151
- let parsed;
7152
- try {
7153
- parsed = JSON.parse(text);
7154
- } catch (err) {
7155
- throw new Error(`Baseline parse failed: ${err.message}. The file must be valid JSON.`);
7156
- }
7157
- if (!parsed || typeof parsed !== "object") {
7158
- throw new Error("Baseline parse failed: expected a JSON object.");
7159
- }
7160
- const obj = parsed;
7161
- if (obj.version !== exports2.BASELINE_VERSION) {
7162
- throw new Error(`Baseline version mismatch: expected ${exports2.BASELINE_VERSION}, got ${String(obj.version)}.`);
7163
- }
7164
- if (!Array.isArray(obj.entries)) {
7165
- throw new Error("Baseline parse failed: 'entries' must be an array.");
7166
- }
7167
- const entries = [];
7168
- for (const raw of obj.entries) {
7169
- if (!raw || typeof raw !== "object")
7170
- continue;
7171
- const e = raw;
7172
- if (typeof e.ruleId !== "string")
7173
- continue;
7174
- if (typeof e.file !== "string")
7175
- continue;
7176
- if (typeof e.message !== "string")
7177
- continue;
7178
- entries.push({ ruleId: e.ruleId, file: e.file, message: e.message });
7179
- }
7180
- const createdAt = typeof obj.createdAt === "string" ? obj.createdAt : "";
7181
- return { version: exports2.BASELINE_VERSION, createdAt, entries };
7182
- }
7183
- function isSecurityCode(code) {
7184
- return !!code && /^S\d{3}$/.test(code);
7185
- }
7186
- function findingKey(d) {
7187
- return `${d.code}\0${normalizeFile(d.location?.file ?? "")}\0${d.message}`;
7188
- }
7189
- function entryKey(e) {
7190
- return `${e.ruleId}\0${normalizeFile(e.file)}\0${e.message}`;
7191
- }
7192
- function compareEntries(a, b) {
7193
- if (a.ruleId !== b.ruleId)
7194
- return a.ruleId < b.ruleId ? -1 : 1;
7195
- if (a.file !== b.file)
7196
- return a.file < b.file ? -1 : 1;
7197
- return a.message < b.message ? -1 : a.message > b.message ? 1 : 0;
7198
- }
7199
- }
7200
- });
7201
-
7202
- // ../typechecker/dist/security-fixer.js
7203
- var require_security_fixer = __commonJS({
7204
- "../typechecker/dist/security-fixer.js"(exports2) {
7205
- "use strict";
7206
- Object.defineProperty(exports2, "__esModule", { value: true });
7207
- exports2.FIXABLE_CODES = void 0;
7208
- exports2.fixFindings = fixFindings;
7209
- exports2.FIXABLE_CODES = /* @__PURE__ */ new Set(["S102", "S109"]);
7210
- function fixFindings(source, findings) {
7211
- const lines = source.split("\n");
7212
- const edits = [];
7213
- const unfixable = [];
7214
- const candidates = [];
7215
- const seen = /* @__PURE__ */ new Set();
7216
- for (const d of findings) {
7217
- const code = d.code;
7218
- if (!code || !code.startsWith("S1"))
7219
- continue;
7220
- if (!exports2.FIXABLE_CODES.has(code)) {
7221
- unfixable.push(d);
7222
- continue;
7223
- }
7224
- const line = d.location?.startLine ?? 0;
7225
- if (line <= 0 || line > lines.length)
7226
- continue;
7227
- const key = `${code}:${line}`;
7228
- if (seen.has(key))
7229
- continue;
7230
- seen.add(key);
7231
- const decorator = decoratorFor(code);
7232
- if (decorator === null) {
7233
- unfixable.push(d);
7234
- continue;
7235
- }
7236
- candidates.push({ code, line, decorator });
7237
- }
7238
- const plannedByLine = /* @__PURE__ */ new Map();
7239
- for (const c of candidates) {
7240
- if (hasDecoratorAbove(lines, c.line, c.decorator))
7241
- continue;
7242
- const planned = plannedByLine.get(c.line) ?? [];
7243
- if (planned.includes(c.decorator))
7244
- continue;
7245
- planned.push(c.decorator);
7246
- plannedByLine.set(c.line, planned);
7247
- }
7248
- const targetLines = [...plannedByLine.keys()].sort((a, b) => b - a);
7249
- for (const line of targetLines) {
7250
- const decorators = plannedByLine.get(line);
7251
- const targetContent = lines[line - 1] ?? "";
7252
- const indent = targetContent.match(/^\s*/)?.[0] ?? "";
7253
- for (const dec of decorators) {
7254
- const inserted = `${indent}${dec}
7255
- `;
7256
- lines.splice(line - 1, 0, `${indent}${dec}`);
7257
- edits.push({
7258
- code: codeForDecorator(dec),
7259
- line,
7260
- inserted
7261
- });
7262
- }
7263
- }
7264
- return {
7265
- source: lines.join("\n"),
7266
- edits,
7267
- unfixable
7268
- };
7269
- }
7270
- function decoratorFor(code) {
7271
- switch (code) {
7272
- case "S102":
7273
- return "@auth";
7274
- case "S109":
7275
- return "@rateLimit(60)";
7276
- default:
7277
- return null;
7278
- }
7279
- }
7280
- function codeForDecorator(decorator) {
7281
- if (decorator === "@auth")
7282
- return "S102";
7283
- if (decorator.startsWith("@rateLimit"))
7284
- return "S109";
7285
- return "";
7286
- }
7287
- function hasDecoratorAbove(lines, targetLine, decorator) {
7288
- const decoratorName = decorator.split("(")[0];
7289
- let cursor = targetLine - 2;
7290
- while (cursor >= 0) {
7291
- const line = lines[cursor]?.trim() ?? "";
7292
- if (line === "") {
7293
- cursor -= 1;
7294
- continue;
7295
- }
7296
- if (!line.startsWith("@"))
7297
- break;
7298
- const existingName = line.split(/[(\s]/)[0];
7299
- if (existingName === decoratorName)
7300
- return true;
7301
- cursor -= 1;
7302
- }
7303
- return false;
7304
- }
7305
- }
7306
- });
7307
-
7308
- // ../typechecker/dist/security-coverage.js
7309
- var require_security_coverage = __commonJS({
7310
- "../typechecker/dist/security-coverage.js"(exports2) {
7311
- "use strict";
7312
- Object.defineProperty(exports2, "__esModule", { value: true });
7313
- exports2.computeCoverage = computeCoverage;
7314
- exports2.renderCoverageTable = renderCoverageTable;
7315
- var COMPLIANCE_TAGS = /* @__PURE__ */ new Set([
7316
- "gdpr",
7317
- "hipaa",
7318
- "soc2",
7319
- "pci",
7320
- "ccpa",
7321
- "pipeda"
7322
- ]);
7323
- var AUTH_DECORATORS = /* @__PURE__ */ new Set([
7324
- "auth",
7325
- "authenticated",
7326
- "authorize",
7327
- "requireAuth",
7328
- "protected"
7329
- ]);
7330
- var RATE_LIMIT_DECORATORS = /* @__PURE__ */ new Set([
7331
- "rateLimit",
7332
- "rate_limit",
7333
- "ratelimit",
7334
- "throttle"
7335
- ]);
7336
- var PII_DECORATORS = /* @__PURE__ */ new Set(["pii", "sensitive"]);
7337
- var SECRET_DECORATORS = /* @__PURE__ */ new Set(["secret"]);
7338
- var ENCRYPT_DECORATORS = /* @__PURE__ */ new Set(["encrypt", "encrypted"]);
7339
- function computeCoverage(programs) {
7340
- const entities = /* @__PURE__ */ new Map();
7341
- const apis = [];
7342
- for (const program of programs) {
7343
- for (const decl of program.body) {
7344
- const unwrapped = decl.type === "ExportDeclaration" ? decl.declaration : decl;
7345
- if (unwrapped.type === "EntityDeclaration") {
7346
- if (!entities.has(unwrapped.name)) {
7347
- entities.set(unwrapped.name, unwrapped);
7348
- }
7349
- } else if (unwrapped.type === "APIDeclaration") {
7350
- apis.push(unwrapped);
7351
- }
7352
- }
7353
- }
7354
- const apisByEntity = /* @__PURE__ */ new Map();
7355
- let orphanApis = 0;
7356
- for (const api of apis) {
7357
- const names = collectEntityNames(api.returnType);
7358
- const matched = [...names].filter((n) => entities.has(n));
7359
- if (matched.length === 0) {
7360
- orphanApis += 1;
7361
- continue;
7362
- }
7363
- for (const name of matched) {
7364
- const arr = apisByEntity.get(name) ?? [];
7365
- arr.push(api);
7366
- apisByEntity.set(name, arr);
7367
- }
7368
- }
7369
- const perEntity = [];
7370
- let totalApis = 0;
7371
- let totalFields = 0;
7372
- let piiFields = 0;
7373
- let secretFields = 0;
7374
- let encryptedFields = 0;
7375
- let entitiesWithCompliance = 0;
7376
- let apisWithAuthTotal = 0;
7377
- let apisWithRateLimitTotal = 0;
7378
- let apisAuditedTotal = 0;
7379
- for (const [name, entity] of [...entities].sort((a, b) => a[0].localeCompare(b[0]))) {
7380
- const entityApis = apisByEntity.get(name) ?? [];
7381
- const entityDecs = decoratorNames(entity.decorators);
7382
- const complianceTags = [...entityDecs].filter((d) => COMPLIANCE_TAGS.has(d)).sort();
7383
- if (complianceTags.length > 0)
7384
- entitiesWithCompliance += 1;
7385
- let ePiiFields = 0;
7386
- let eSecretFields = 0;
7387
- let eEncryptedFields = 0;
7388
- for (const field of entity.fields) {
7389
- const fieldDecs = decoratorNames(field.decorators);
7390
- if (hasAnyOf(fieldDecs, PII_DECORATORS))
7391
- ePiiFields += 1;
7392
- if (hasAnyOf(fieldDecs, SECRET_DECORATORS))
7393
- eSecretFields += 1;
7394
- if (hasAnyOf(fieldDecs, ENCRYPT_DECORATORS))
7395
- eEncryptedFields += 1;
7396
- }
7397
- let apisWithAuth = 0;
7398
- let apisWithRateLimit = 0;
7399
- let auditedApis = 0;
7400
- for (const api of entityApis) {
7401
- const apiDecs = decoratorNames(api.decorators);
7402
- if (hasAnyOf(apiDecs, AUTH_DECORATORS))
7403
- apisWithAuth += 1;
7404
- if (hasAnyOf(apiDecs, RATE_LIMIT_DECORATORS))
7405
- apisWithRateLimit += 1;
7406
- if (apiDecs.has("audit"))
7407
- auditedApis += 1;
7408
- }
7409
- perEntity.push({
7410
- name,
7411
- complianceTags,
7412
- apiCount: entityApis.length,
7413
- apisWithAuth,
7414
- apisWithRateLimit,
7415
- fieldCount: entity.fields.length,
7416
- piiFields: ePiiFields,
7417
- secretFields: eSecretFields,
7418
- encryptedFields: eEncryptedFields,
7419
- auditedApis
7420
- });
7421
- totalApis += entityApis.length;
7422
- totalFields += entity.fields.length;
7423
- piiFields += ePiiFields;
7424
- secretFields += eSecretFields;
7425
- encryptedFields += eEncryptedFields;
7426
- apisWithAuthTotal += apisWithAuth;
7427
- apisWithRateLimitTotal += apisWithRateLimit;
7428
- apisAuditedTotal += auditedApis;
7429
- }
7430
- const summary = {
7431
- totalEntities: entities.size,
7432
- totalApis,
7433
- totalFields,
7434
- entitiesWithCompliance,
7435
- apisWithAuth: apisWithAuthTotal,
7436
- apisWithRateLimit: apisWithRateLimitTotal,
7437
- apisAudited: apisAuditedTotal,
7438
- fieldsProtected: piiFields + secretFields + encryptedFields,
7439
- piiFields,
7440
- secretFields,
7441
- encryptedFields
7442
- };
7443
- return { entities: perEntity, summary, orphanApis };
7444
- }
7445
- function renderCoverageTable(report) {
7446
- const rows = [];
7447
- rows.push([
7448
- "Entity",
7449
- "Tags",
7450
- "APIs",
7451
- "w/ @auth",
7452
- "w/ @rate",
7453
- "w/ @audit",
7454
- "Fields",
7455
- "@pii",
7456
- "@secret",
7457
- "@encrypt"
7458
- ]);
7459
- for (const e of report.entities) {
7460
- rows.push([
7461
- e.name,
7462
- e.complianceTags.length === 0 ? "-" : e.complianceTags.map((t) => `@${t}`).join(" "),
7463
- String(e.apiCount),
7464
- ratio(e.apisWithAuth, e.apiCount),
7465
- ratio(e.apisWithRateLimit, e.apiCount),
7466
- ratio(e.auditedApis, e.apiCount),
7467
- String(e.fieldCount),
7468
- String(e.piiFields),
7469
- String(e.secretFields),
7470
- String(e.encryptedFields)
7471
- ]);
7472
- }
7473
- const widths = computeWidths(rows);
7474
- const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
7475
- const lines = [];
7476
- lines.push(`Security coverage \u2014 ${report.summary.totalEntities} entities, ${report.summary.totalApis} APIs, ${report.summary.totalFields} fields`);
7477
- lines.push("");
7478
- lines.push(pad(rows[0]));
7479
- lines.push(widths.map((w) => "-".repeat(w)).join(" "));
7480
- for (const row of rows.slice(1))
7481
- lines.push(pad(row));
7482
- lines.push("");
7483
- lines.push("Rollup:");
7484
- lines.push(` entities with compliance tag: ${summaryRatio(report.summary.entitiesWithCompliance, report.summary.totalEntities)}`);
7485
- lines.push(` APIs with @auth: ${summaryRatio(report.summary.apisWithAuth, report.summary.totalApis)}`);
7486
- lines.push(` APIs with @rateLimit: ${summaryRatio(report.summary.apisWithRateLimit, report.summary.totalApis)}`);
7487
- lines.push(` APIs with @audit: ${summaryRatio(report.summary.apisAudited, report.summary.totalApis)}`);
7488
- lines.push(` fields with any protection: ${summaryRatio(report.summary.fieldsProtected, report.summary.totalFields)}`);
7489
- if (report.orphanApis > 0) {
7490
- lines.push(` APIs returning a non-entity: ${report.orphanApis} (not counted in entity rows)`);
7491
- }
7492
- return lines.join("\n") + "\n";
7493
- }
7494
- function decoratorNames(decorators) {
7495
- const out = /* @__PURE__ */ new Set();
7496
- for (const d of decorators ?? [])
7497
- out.add(d.name);
7498
- return out;
7499
- }
7500
- function hasAnyOf(names, required) {
7501
- for (const name of required) {
7502
- if (names.has(name))
7503
- return true;
7504
- }
7505
- return false;
7506
- }
7507
- function collectEntityNames(expr) {
7508
- const out = /* @__PURE__ */ new Set();
7509
- const walk = (t) => {
7510
- if (t.type === "TypeReference")
7511
- out.add(t.name);
7512
- if (t.type === "ArrayType")
7513
- walk(t.elementType);
7514
- if (t.type === "UnionType" && "types" in t) {
7515
- for (const sub of t.types)
7516
- walk(sub);
7517
- }
7518
- };
7519
- walk(expr);
7520
- return out;
7521
- }
7522
- function ratio(n, total) {
7523
- if (total === 0)
7524
- return "\u2014";
7525
- return `${n}/${total}`;
7526
- }
7527
- function summaryRatio(n, total) {
7528
- if (total === 0)
7529
- return "\u2014 (n/a)";
7530
- const pct = Math.round(n / total * 100);
7531
- return `${n}/${total} (${pct}%)`;
7532
- }
7533
- function computeWidths(rows) {
7534
- if (rows.length === 0)
7535
- return [];
7536
- const widths = new Array(rows[0].length).fill(0);
7537
- for (const row of rows) {
7538
- for (let i = 0; i < row.length; i += 1) {
7539
- widths[i] = Math.max(widths[i], row[i].length);
7540
- }
7541
- }
7542
- return widths;
7543
- }
7544
- }
7545
- });
7546
-
7547
- // ../typechecker/dist/index.js
7548
- var require_dist4 = __commonJS({
7549
- "../typechecker/dist/index.js"(exports2) {
7550
- "use strict";
7551
- Object.defineProperty(exports2, "__esModule", { value: true });
7552
- exports2.renderCoverageTable = exports2.computeCoverage = exports2.FIXABLE_CODES = exports2.fixFindings = exports2.BASELINE_VERSION = exports2.parseBaseline = exports2.serializeBaseline = exports2.applyBaseline = exports2.createBaseline = exports2.TYPERION_AUDITOR_NAME = exports2.SARIF_VERSION = exports2.SARIF_SCHEMA_URI = exports2.RULE_CATALOGUE = exports2.renderSarif = exports2.SecurityAuditor = exports2.listErrorCodes = exports2.explainErrorCode = exports2.getErrorCode = exports2.ERROR_CODES = exports2.SystemChecker = exports2.BehaviorChecker = exports2.CompletenessChecker = exports2.Scope = exports2.TypeChecker = void 0;
7553
- var checker_js_1 = require_checker();
7554
- Object.defineProperty(exports2, "TypeChecker", { enumerable: true, get: function() {
7555
- return checker_js_1.TypeChecker;
7556
- } });
7557
- var scope_js_1 = require_scope();
7558
- Object.defineProperty(exports2, "Scope", { enumerable: true, get: function() {
7559
- return scope_js_1.Scope;
7560
- } });
7561
- var completeness_checker_js_1 = require_completeness_checker();
7562
- Object.defineProperty(exports2, "CompletenessChecker", { enumerable: true, get: function() {
7563
- return completeness_checker_js_1.CompletenessChecker;
7564
- } });
7565
- var behavior_checker_js_1 = require_behavior_checker();
7566
- Object.defineProperty(exports2, "BehaviorChecker", { enumerable: true, get: function() {
7567
- return behavior_checker_js_1.BehaviorChecker;
7568
- } });
7569
- var system_checker_js_1 = require_system_checker();
7570
- Object.defineProperty(exports2, "SystemChecker", { enumerable: true, get: function() {
7571
- return system_checker_js_1.SystemChecker;
7572
- } });
7573
- var error_codes_js_1 = require_error_codes();
7574
- Object.defineProperty(exports2, "ERROR_CODES", { enumerable: true, get: function() {
7575
- return error_codes_js_1.ERROR_CODES;
7576
- } });
7577
- Object.defineProperty(exports2, "getErrorCode", { enumerable: true, get: function() {
7578
- return error_codes_js_1.getErrorCode;
7579
- } });
7580
- Object.defineProperty(exports2, "explainErrorCode", { enumerable: true, get: function() {
7581
- return error_codes_js_1.explainErrorCode;
7582
- } });
7583
- Object.defineProperty(exports2, "listErrorCodes", { enumerable: true, get: function() {
7584
- return error_codes_js_1.listErrorCodes;
7585
- } });
7586
- var security_auditor_js_1 = require_security_auditor();
7587
- Object.defineProperty(exports2, "SecurityAuditor", { enumerable: true, get: function() {
7588
- return security_auditor_js_1.SecurityAuditor;
7589
- } });
7590
- var security_auditor_sarif_js_1 = require_security_auditor_sarif();
7591
- Object.defineProperty(exports2, "renderSarif", { enumerable: true, get: function() {
7592
- return security_auditor_sarif_js_1.renderSarif;
7593
- } });
7594
- Object.defineProperty(exports2, "RULE_CATALOGUE", { enumerable: true, get: function() {
7595
- return security_auditor_sarif_js_1.RULE_CATALOGUE;
7596
- } });
7597
- Object.defineProperty(exports2, "SARIF_SCHEMA_URI", { enumerable: true, get: function() {
7598
- return security_auditor_sarif_js_1.SARIF_SCHEMA_URI;
7599
- } });
7600
- Object.defineProperty(exports2, "SARIF_VERSION", { enumerable: true, get: function() {
7601
- return security_auditor_sarif_js_1.SARIF_VERSION;
7602
- } });
7603
- Object.defineProperty(exports2, "TYPERION_AUDITOR_NAME", { enumerable: true, get: function() {
7604
- return security_auditor_sarif_js_1.TYPERION_AUDITOR_NAME;
7605
- } });
7606
- var security_baseline_js_1 = require_security_baseline();
7607
- Object.defineProperty(exports2, "createBaseline", { enumerable: true, get: function() {
7608
- return security_baseline_js_1.createBaseline;
7609
- } });
7610
- Object.defineProperty(exports2, "applyBaseline", { enumerable: true, get: function() {
7611
- return security_baseline_js_1.applyBaseline;
7612
- } });
7613
- Object.defineProperty(exports2, "serializeBaseline", { enumerable: true, get: function() {
7614
- return security_baseline_js_1.serializeBaseline;
7615
- } });
7616
- Object.defineProperty(exports2, "parseBaseline", { enumerable: true, get: function() {
7617
- return security_baseline_js_1.parseBaseline;
7618
- } });
7619
- Object.defineProperty(exports2, "BASELINE_VERSION", { enumerable: true, get: function() {
7620
- return security_baseline_js_1.BASELINE_VERSION;
7621
- } });
7622
- var security_fixer_js_1 = require_security_fixer();
7623
- Object.defineProperty(exports2, "fixFindings", { enumerable: true, get: function() {
7624
- return security_fixer_js_1.fixFindings;
7625
- } });
7626
- Object.defineProperty(exports2, "FIXABLE_CODES", { enumerable: true, get: function() {
7627
- return security_fixer_js_1.FIXABLE_CODES;
7628
- } });
7629
- var security_coverage_js_1 = require_security_coverage();
7630
- Object.defineProperty(exports2, "computeCoverage", { enumerable: true, get: function() {
7631
- return security_coverage_js_1.computeCoverage;
7632
- } });
7633
- Object.defineProperty(exports2, "renderCoverageTable", { enumerable: true, get: function() {
7634
- return security_coverage_js_1.renderCoverageTable;
7635
- } });
7636
- }
7637
- });
7638
-
7639
5851
  // ../codegen/dist/ir.js
7640
5852
  var require_ir = __commonJS({
7641
5853
  "../codegen/dist/ir.js"(exports2) {
@@ -11704,296 +9916,14 @@ var require_typescript_printer = __commonJS({
11704
9916
  }
11705
9917
  });
11706
9918
 
11707
- // ../codegen/dist/language-ast/graphql-builder.js
11708
- var require_graphql_builder = __commonJS({
11709
- "../codegen/dist/language-ast/graphql-builder.js"(exports2) {
11710
- "use strict";
11711
- Object.defineProperty(exports2, "__esModule", { value: true });
11712
- exports2.GraphQL = void 0;
11713
- exports2.gqlNamed = gqlNamed;
11714
- exports2.gqlList = gqlList;
11715
- exports2.gqlNonNull = gqlNonNull;
11716
- exports2.gqlArg = gqlArg;
11717
- exports2.gqlField = gqlField;
11718
- exports2.gqlObjectType = gqlObjectType;
11719
- exports2.gqlInputType = gqlInputType;
11720
- exports2.gqlEnumValue = gqlEnumValue;
11721
- exports2.gqlEnum = gqlEnum;
11722
- exports2.gqlQuery = gqlQuery;
11723
- exports2.gqlMutation = gqlMutation;
11724
- exports2.gqlComment = gqlComment;
11725
- var base_js_1 = require_base();
11726
- function gqlNamed(name) {
11727
- return { kind: "g-named-type", name: (0, base_js_1.ident)(name) };
11728
- }
11729
- function gqlList(inner) {
11730
- return { kind: "g-list-type", inner };
11731
- }
11732
- function gqlNonNull(inner) {
11733
- return { kind: "g-nonnull-type", inner };
11734
- }
11735
- function gqlArg(name, type) {
11736
- return { kind: "g-arg", name: (0, base_js_1.ident)(name), type };
11737
- }
11738
- function gqlField(args) {
11739
- return {
11740
- kind: "g-field",
11741
- name: (0, base_js_1.ident)(args.name),
11742
- type: args.type,
11743
- args: args.args ?? []
11744
- };
11745
- }
11746
- function gqlObjectType(args) {
11747
- return { kind: "g-object-type", name: (0, base_js_1.ident)(args.name), fields: args.fields };
11748
- }
11749
- function gqlInputType(args) {
11750
- return { kind: "g-input-type", name: (0, base_js_1.ident)(args.name), fields: args.fields };
11751
- }
11752
- function gqlEnumValue(name) {
11753
- return { kind: "g-enum-value", name: (0, base_js_1.ident)(name) };
11754
- }
11755
- function gqlEnum(args) {
11756
- return { kind: "g-enum", name: (0, base_js_1.ident)(args.name), values: args.values };
11757
- }
11758
- function gqlQuery(fields) {
11759
- return { kind: "g-query", fields };
11760
- }
11761
- function gqlMutation(fields) {
11762
- return { kind: "g-mutation", fields };
11763
- }
11764
- function gqlComment(text) {
11765
- return { kind: "g-comment", text };
11766
- }
11767
- exports2.GraphQL = {
11768
- named: gqlNamed,
11769
- list: gqlList,
11770
- nonNull: gqlNonNull,
11771
- arg: gqlArg,
11772
- field: gqlField,
11773
- objectType: gqlObjectType,
11774
- inputType: gqlInputType,
11775
- enumValue: gqlEnumValue,
11776
- enum: gqlEnum,
11777
- query: gqlQuery,
11778
- mutation: gqlMutation,
11779
- comment: gqlComment
11780
- };
11781
- }
11782
- });
11783
-
11784
- // ../codegen/dist/language-ast/graphql-serializer.js
11785
- var require_graphql_serializer = __commonJS({
11786
- "../codegen/dist/language-ast/graphql-serializer.js"(exports2) {
11787
- "use strict";
11788
- Object.defineProperty(exports2, "__esModule", { value: true });
11789
- exports2.graphqlSerializer = exports2.GraphQLSerializer = void 0;
11790
- var base_js_1 = require_base();
11791
- var KW_TYPE = "type";
11792
- var KW_INPUT = "input";
11793
- var KW_ENUM = "enum";
11794
- var KW_QUERY = "Query";
11795
- var KW_MUTATION = "Mutation";
11796
- var SPACE = " ";
11797
- var NL = "\n";
11798
- var COMMA = ", ";
11799
- var COLON = ": ";
11800
- var OPEN_BRACE = "{";
11801
- var CLOSE_BRACE = "}";
11802
- var OPEN_PAREN = "(";
11803
- var CLOSE_PAREN = ")";
11804
- var OPEN_BRACKET = "[";
11805
- var CLOSE_BRACKET = "]";
11806
- var BANG = "!";
11807
- var HASH = "# ";
11808
- var GraphQLSerializer = class {
11809
- language = "graphql";
11810
- serialize(node, ctx = (0, base_js_1.makeContext)()) {
11811
- switch (node.kind) {
11812
- case "g-named-type":
11813
- return node.name;
11814
- case "g-list-type":
11815
- return OPEN_BRACKET + this.serialize(node.inner) + CLOSE_BRACKET;
11816
- case "g-nonnull-type":
11817
- return this.serialize(node.inner) + BANG;
11818
- case "g-field":
11819
- return this.field(node);
11820
- case "g-arg":
11821
- return this.arg(node);
11822
- case "g-object-type":
11823
- return this.objectType(node, ctx);
11824
- case "g-input-type":
11825
- return this.inputType(node, ctx);
11826
- case "g-enum":
11827
- return this.enumDecl(node, ctx);
11828
- case "g-enum-value":
11829
- return node.name;
11830
- case "g-query":
11831
- return this.queryDecl(node, ctx);
11832
- case "g-mutation":
11833
- return this.mutationDecl(node, ctx);
11834
- case "g-comment":
11835
- return this.comment(node);
11836
- case "raw":
11837
- return node.text;
11838
- default:
11839
- throw new Error("GraphQLSerializer: unknown " + node.kind);
11840
- }
11841
- }
11842
- field(node) {
11843
- const args = node.args.length > 0 ? OPEN_PAREN + node.args.map((a) => this.arg(a)).join(COMMA) + CLOSE_PAREN : "";
11844
- return node.name + args + COLON + this.serialize(node.type);
11845
- }
11846
- arg(node) {
11847
- return node.name + COLON + this.serialize(node.type);
11848
- }
11849
- objectType(node, ctx) {
11850
- return this.blockDecl(KW_TYPE, node.name, node.fields, ctx);
11851
- }
11852
- inputType(node, ctx) {
11853
- return this.blockDecl(KW_INPUT, node.name, node.fields, ctx);
11854
- }
11855
- blockDecl(keyword, name, fields, ctx) {
11856
- const inner = ctx.indented();
11857
- const prefix = inner.indentString.repeat(inner.indent);
11858
- const lines = [];
11859
- for (const f of fields)
11860
- lines.push(prefix + this.field(f));
11861
- return keyword + SPACE + name + SPACE + OPEN_BRACE + NL + lines.join(NL) + NL + CLOSE_BRACE;
11862
- }
11863
- enumDecl(node, ctx) {
11864
- const inner = ctx.indented();
11865
- const prefix = inner.indentString.repeat(inner.indent);
11866
- const lines = [];
11867
- for (const v of node.values)
11868
- lines.push(prefix + v.name);
11869
- return KW_ENUM + SPACE + node.name + SPACE + OPEN_BRACE + NL + lines.join(NL) + NL + CLOSE_BRACE;
11870
- }
11871
- queryDecl(node, ctx) {
11872
- return this.blockDecl(KW_TYPE, KW_QUERY, node.fields, ctx);
11873
- }
11874
- mutationDecl(node, ctx) {
11875
- return this.blockDecl(KW_TYPE, KW_MUTATION, node.fields, ctx);
11876
- }
11877
- comment(node) {
11878
- const lines = node.text.split(NL);
11879
- const out = [];
11880
- for (const l of lines)
11881
- out.push(HASH + l);
11882
- return (0, base_js_1.joinLines)(out, NL);
11883
- }
11884
- };
11885
- exports2.GraphQLSerializer = GraphQLSerializer;
11886
- exports2.graphqlSerializer = new GraphQLSerializer();
11887
- }
11888
- });
11889
-
11890
- // ../codegen/dist/printers/graphql-printer.js
11891
- var require_graphql_printer = __commonJS({
11892
- "../codegen/dist/printers/graphql-printer.js"(exports2) {
11893
- "use strict";
11894
- Object.defineProperty(exports2, "__esModule", { value: true });
11895
- exports2.GraphQLPrinter = void 0;
11896
- var graphql_builder_js_1 = require_graphql_builder();
11897
- var graphql_serializer_js_1 = require_graphql_serializer();
11898
- var base_js_1 = require_base();
11899
- function toGQLType(typeStr) {
11900
- if (typeStr.endsWith("[]")) {
11901
- return graphql_builder_js_1.GraphQL.list(graphql_builder_js_1.GraphQL.nonNull(toGQLType(typeStr.slice(0, -2))));
11902
- }
11903
- return graphql_builder_js_1.GraphQL.named(typeStr);
11904
- }
11905
- function withNonNull(type, nonNull) {
11906
- return nonNull ? graphql_builder_js_1.GraphQL.nonNull(type) : type;
11907
- }
11908
- function transformField(f) {
11909
- const base = toGQLType(f.type);
11910
- const type = withNonNull(base, !f.optional);
11911
- return graphql_builder_js_1.GraphQL.field({ name: f.name, type });
11912
- }
11913
- function transformMethod(m) {
11914
- const args = [];
11915
- for (const p of m.params) {
11916
- const base = toGQLType(p.type);
11917
- args.push(graphql_builder_js_1.GraphQL.arg(p.name, withNonNull(base, !p.optional)));
11918
- }
11919
- const returnType = withNonNull(toGQLType(m.returnType), true);
11920
- return graphql_builder_js_1.GraphQL.field({ name: m.name, type: returnType, args });
11921
- }
11922
- function printNode(node) {
11923
- switch (node.nodeKind) {
11924
- case "interface": {
11925
- const fields = [];
11926
- for (const f of node.fields)
11927
- fields.push(transformField(f));
11928
- if (node.methods) {
11929
- for (const m of node.methods)
11930
- fields.push(transformMethod(m));
11931
- }
11932
- const keyword = node.keyword;
11933
- if (keyword === "input") {
11934
- return graphql_serializer_js_1.graphqlSerializer.serialize(graphql_builder_js_1.GraphQL.inputType({ name: node.name, fields }));
11935
- }
11936
- if (keyword === "Query") {
11937
- return graphql_serializer_js_1.graphqlSerializer.serialize(graphql_builder_js_1.GraphQL.query(fields));
11938
- }
11939
- if (keyword === "Mutation") {
11940
- return graphql_serializer_js_1.graphqlSerializer.serialize(graphql_builder_js_1.GraphQL.mutation(fields));
11941
- }
11942
- return graphql_serializer_js_1.graphqlSerializer.serialize(graphql_builder_js_1.GraphQL.objectType({ name: node.name, fields }));
11943
- }
11944
- case "enum": {
11945
- const values = node.variants.map((v) => graphql_builder_js_1.GraphQL.enumValue(v.name));
11946
- return graphql_serializer_js_1.graphqlSerializer.serialize(graphql_builder_js_1.GraphQL.enum({ name: node.name, values }));
11947
- }
11948
- case "field": {
11949
- return graphql_serializer_js_1.graphqlSerializer.serialize(transformField(node));
11950
- }
11951
- case "method": {
11952
- return graphql_serializer_js_1.graphqlSerializer.serialize(transformMethod(node));
11953
- }
11954
- case "comment": {
11955
- return graphql_serializer_js_1.graphqlSerializer.serialize(graphql_builder_js_1.GraphQL.comment(node.text));
11956
- }
11957
- case "raw": {
11958
- return node.code;
11959
- }
11960
- case "function":
11961
- case "file":
11962
- case "import":
11963
- case "param":
11964
- case "enum-variant":
11965
- case "type-alias":
11966
- case "class":
11967
- case "const":
11968
- default: {
11969
- return graphql_serializer_js_1.graphqlSerializer.serialize((0, base_js_1.raw)("# unknown: " + node.nodeKind, "unsupported-feature"));
11970
- }
11971
- }
11972
- }
11973
- exports2.GraphQLPrinter = {
11974
- target: "graphql",
11975
- print(node) {
11976
- return printNode(node);
11977
- },
11978
- printFile(file) {
11979
- const parts = [];
11980
- for (const n of file.nodes)
11981
- parts.push(printNode(n));
11982
- return parts.join("\n\n");
11983
- }
11984
- };
11985
- }
11986
- });
11987
-
11988
9919
  // ../codegen/dist/codegen-contract/ast-builders.js
11989
9920
  var require_ast_builders = __commonJS({
11990
9921
  "../codegen/dist/codegen-contract/ast-builders.js"(exports2) {
11991
9922
  "use strict";
11992
9923
  Object.defineProperty(exports2, "__esModule", { value: true });
11993
- exports2.GraphQLPrinter = exports2.TypeScriptPrinter = exports2.CodeBuilder = void 0;
9924
+ exports2.TypeScriptPrinter = exports2.CodeBuilder = void 0;
11994
9925
  exports2.nodeToString = nodeToString;
11995
9926
  var typescript_printer_js_1 = require_typescript_printer();
11996
- var graphql_printer_js_1 = require_graphql_printer();
11997
9927
  exports2.CodeBuilder = {
11998
9928
  /** Create a file containing code nodes. */
11999
9929
  file(path2, nodes) {
@@ -12126,7 +10056,6 @@ var require_ast_builders = __commonJS({
12126
10056
  }
12127
10057
  };
12128
10058
  exports2.TypeScriptPrinter = typescript_printer_js_1.TypeScriptPrinter;
12129
- exports2.GraphQLPrinter = graphql_printer_js_1.GraphQLPrinter;
12130
10059
  function nodeToString(node) {
12131
10060
  if (node.nodeKind === "raw")
12132
10061
  return node.code;
@@ -13868,7 +11797,7 @@ var fs = __toESM(require("node:fs"));
13868
11797
  var path = __toESM(require("node:path"));
13869
11798
  var import_lexer = __toESM(require_dist2());
13870
11799
  var import_parser = __toESM(require_dist3());
13871
- var import_typechecker = __toESM(require_dist4());
11800
+ var import_checker = __toESM(require_checker());
13872
11801
  var import_ir_compiler = __toESM(require_ir_compiler());
13873
11802
  var import_ts_codegen = __toESM(require_ts_codegen());
13874
11803
  var import_openapi_codegen = __toESM(require_openapi_codegen());
@@ -13888,7 +11817,7 @@ function parseToProgram(source, file) {
13888
11817
  return program;
13889
11818
  }
13890
11819
  function typecheckOrDie(program, file) {
13891
- const { diagnostics } = new import_typechecker.TypeChecker().check(program, file);
11820
+ const { diagnostics } = new import_checker.TypeChecker().check(program, file);
13892
11821
  const errors = diagnostics.filter((d) => d.severity === "error");
13893
11822
  if (errors.length > 0) {
13894
11823
  console.error(`\u2717 ${file}:`);