@terpjs/spec 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +417 -417
  3. package/VERSION +1 -1
  4. package/app-check-report.schema.json +122 -122
  5. package/assurance-profile.schema.json +70 -70
  6. package/catalog/backend/alembic_downgrades_not_empty.json +22 -22
  7. package/catalog/backend/base_query_not_overridden.json +27 -27
  8. package/catalog/backend/canonical_module_shape.json +22 -22
  9. package/catalog/backend/datetime_columns_are_timezone_aware.json +23 -23
  10. package/catalog/backend/escape_hatch_budget.json +21 -21
  11. package/catalog/backend/events_reference_catalog.json +27 -27
  12. package/catalog/backend/input_schemas_exclude_managed_columns.json +27 -27
  13. package/catalog/backend/input_str_fields_have_max_length.json +22 -22
  14. package/catalog/backend/jobs_reference_catalog.json +27 -27
  15. package/catalog/backend/list_routes_paginate.json +32 -32
  16. package/catalog/backend/modules_declare_policy.json +26 -26
  17. package/catalog/backend/mutations_emit_audit.json +26 -26
  18. package/catalog/backend/mutations_require_write_role.json +32 -32
  19. package/catalog/backend/no_adhoc_background_runtime.json +22 -22
  20. package/catalog/backend/no_adhoc_config_decrypt.json +27 -27
  21. package/catalog/backend/no_adhoc_logging_config.json +22 -22
  22. package/catalog/backend/no_adhoc_middleware.json +27 -27
  23. package/catalog/backend/no_adhoc_permission_literals.json +27 -27
  24. package/catalog/backend/no_app_instantiation.json +22 -22
  25. package/catalog/backend/no_blocking_sleep.json +22 -22
  26. package/catalog/backend/no_cross_module_imports.json +21 -21
  27. package/catalog/backend/no_dependency_overrides.json +27 -27
  28. package/catalog/backend/no_destructive_migrations.json +22 -22
  29. package/catalog/backend/no_dynamic_sql.json +22 -22
  30. package/catalog/backend/no_empty_tests.json +22 -22
  31. package/catalog/backend/no_eval_or_exec.json +22 -22
  32. package/catalog/backend/no_hardcoded_credentials.json +21 -21
  33. package/catalog/backend/no_internal_imports.json +22 -22
  34. package/catalog/backend/no_manual_actor_stamping.json +27 -27
  35. package/catalog/backend/no_manual_ownership_checks.json +32 -32
  36. package/catalog/backend/no_manual_scope_filtering.json +27 -27
  37. package/catalog/backend/no_manual_table_schema.json +22 -22
  38. package/catalog/backend/no_manual_version_assignment.json +22 -22
  39. package/catalog/backend/no_mutable_default_args.json +22 -22
  40. package/catalog/backend/no_naive_datetime.json +22 -22
  41. package/catalog/backend/no_oversized_python_files.json +22 -22
  42. package/catalog/backend/no_print.json +22 -22
  43. package/catalog/backend/no_raw_app_routes.json +26 -26
  44. package/catalog/backend/no_raw_connection_access.json +27 -27
  45. package/catalog/backend/no_raw_file_references.json +27 -27
  46. package/catalog/backend/no_raw_outbound_http.json +22 -22
  47. package/catalog/backend/no_raw_session_construction.json +22 -22
  48. package/catalog/backend/no_star_imports.json +22 -22
  49. package/catalog/backend/no_todo_fixme.json +22 -22
  50. package/catalog/backend/no_unique_columns_on_soft_delete_models.json +22 -22
  51. package/catalog/backend/offset_queries_declare_ordering.json +22 -22
  52. package/catalog/backend/path_id_params_are_uuid.json +22 -22
  53. package/catalog/backend/policy_refs_resolve.json +27 -27
  54. package/catalog/backend/public_modules_are_read_only.json +26 -26
  55. package/catalog/backend/reads_use_base_query.json +27 -27
  56. package/catalog/backend/response_model_not_table_model.json +27 -27
  57. package/catalog/backend/routes_declare_response_model.json +27 -27
  58. package/catalog/backend/safe_methods_are_read_only.json +31 -31
  59. package/catalog/backend/schemas_exclude_sensitive_fields.json +32 -32
  60. package/catalog/backend/session_imported_from_sqlmodel.json +22 -22
  61. package/catalog/backend/table_models_use_base_table.json +22 -22
  62. package/catalog/backend/tables_have_migrations.json +27 -27
  63. package/catalog/backend/tenant_scoped_models_use_scoped_service.json +22 -22
  64. package/catalog/backend/ungoverned_escape_hatch.json +21 -21
  65. package/catalog/backend/update_schemas_inherit_base_update_schema.json +32 -32
  66. package/catalog/frontend/escape-hatch.json +21 -21
  67. package/catalog/frontend/generated-client-only.json +26 -26
  68. package/catalog/frontend/layout-contract.json +26 -26
  69. package/catalog/frontend/no-cross-module-imports.json +21 -21
  70. package/catalog/frontend/no-deep-imports.json +25 -25
  71. package/catalog/frontend/no-dom-html-injection.json +22 -22
  72. package/catalog/frontend/no-eval.json +21 -21
  73. package/catalog/frontend/no-inline-styling.json +25 -25
  74. package/catalog/frontend/no-style-imports.json +24 -24
  75. package/catalog/frontend/no-unsafe-href.json +21 -21
  76. package/catalog/frontend/no-unsafe-target-blank.json +21 -21
  77. package/catalog/frontend/router-links.json +22 -22
  78. package/catalog/frontend/token-styled-elements.json +25 -25
  79. package/catalog/schema.json +95 -95
  80. package/corpus/PENDING.json +4 -4
  81. package/corpus/RESIDUALS.json +21 -21
  82. package/corpus/backend/alembic_downgrades_not_empty/compliant-01/modules/notes/migrations/versions/0001_change.py +6 -6
  83. package/corpus/backend/alembic_downgrades_not_empty/violation-01/modules/notes/migrations/versions/0001_change.py +6 -6
  84. package/corpus/backend/base_query_not_overridden/compliant-01/modules/notes/service.py +8 -8
  85. package/corpus/backend/base_query_not_overridden/compliant-02/modules/notes/service.py +8 -8
  86. package/corpus/backend/base_query_not_overridden/violation-01/modules/notes/service.py +8 -8
  87. package/corpus/backend/base_query_not_overridden/violation-02/expected-findings.json +7 -7
  88. package/corpus/backend/base_query_not_overridden/violation-02/modules/notes/service.py +8 -8
  89. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/models.py +6 -6
  90. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/module.py +7 -7
  91. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/router.py +3 -3
  92. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/schemas.py +10 -10
  93. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/service.py +5 -5
  94. package/corpus/backend/canonical_module_shape/violation-01/modules/notes/router.py +3 -3
  95. package/corpus/backend/datetime_columns_are_timezone_aware/compliant-01/modules/notes/models.py +12 -12
  96. package/corpus/backend/datetime_columns_are_timezone_aware/violation-01/modules/notes/models.py +12 -12
  97. package/corpus/backend/datetime_columns_are_timezone_aware/violation-02/modules/notes/models.py +13 -13
  98. package/corpus/backend/datetime_columns_are_timezone_aware/violation-03/modules/notes/models.py +15 -15
  99. package/corpus/backend/escape_hatch_budget/compliant-01/escape-hatch-budget.json +3 -3
  100. package/corpus/backend/escape_hatch_budget/compliant-01/modules/notes/service.py +2 -2
  101. package/corpus/backend/escape_hatch_budget/compliant-02/escape-hatch-budget.json +1 -1
  102. package/corpus/backend/escape_hatch_budget/compliant-02/modules/notes/service.py +4 -4
  103. package/corpus/backend/escape_hatch_budget/violation-01/escape-hatch-budget.json +1 -1
  104. package/corpus/backend/escape_hatch_budget/violation-01/modules/notes/service.py +2 -2
  105. package/corpus/backend/escape_hatch_budget/violation-02/escape-hatch-budget.json +3 -3
  106. package/corpus/backend/escape_hatch_budget/violation-02/modules/notes/service.py +4 -4
  107. package/corpus/backend/events_reference_catalog/compliant-01/modules/notes/module.py +9 -9
  108. package/corpus/backend/events_reference_catalog/violation-01/modules/notes/module.py +8 -8
  109. package/corpus/backend/input_schemas_exclude_managed_columns/compliant-01/modules/notes/schemas.py +6 -6
  110. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/expected-findings.json +7 -7
  111. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/modules/notes/schemas.py +9 -9
  112. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/expected-findings.json +12 -12
  113. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/modules/notes/router.py +14 -14
  114. package/corpus/backend/input_str_fields_have_max_length/compliant-01/modules/notes/schemas.py +4 -4
  115. package/corpus/backend/input_str_fields_have_max_length/violation-01/modules/notes/schemas.py +2 -2
  116. package/corpus/backend/jobs_reference_catalog/compliant-01/modules/notes/service.py +5 -5
  117. package/corpus/backend/jobs_reference_catalog/violation-01/modules/notes/service.py +2 -2
  118. package/corpus/backend/list_routes_paginate/compliant-01/modules/notes/router.py +6 -6
  119. package/corpus/backend/list_routes_paginate/violation-01/modules/notes/router.py +3 -3
  120. package/corpus/backend/modules_declare_policy/compliant-01/modules/notes/module.py +7 -7
  121. package/corpus/backend/modules_declare_policy/violation-01/modules/notes/module.py +6 -6
  122. package/corpus/backend/mutations_emit_audit/compliant-01/modules/notes/service.py +9 -9
  123. package/corpus/backend/mutations_emit_audit/violation-01/modules/notes/service.py +4 -4
  124. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/module.py +7 -7
  125. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/router.py +3 -3
  126. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/module.py +7 -7
  127. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/router.py +3 -3
  128. package/corpus/backend/mutations_require_write_role/violation-01/expected-findings.json +7 -7
  129. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/module.py +7 -7
  130. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/router.py +3 -3
  131. package/corpus/backend/mutations_require_write_role/violation-02/expected-findings.json +7 -7
  132. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/module.py +7 -7
  133. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/router.py +5 -5
  134. package/corpus/backend/mutations_require_write_role/violation-03/expected-findings.json +7 -7
  135. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/module.py +10 -10
  136. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/router.py +3 -3
  137. package/corpus/backend/no_adhoc_background_runtime/compliant-01/modules/notes/service.py +9 -9
  138. package/corpus/backend/no_adhoc_background_runtime/violation-01/modules/notes/service.py +5 -5
  139. package/corpus/backend/no_adhoc_background_runtime/violation-02/modules/notes/service.py +3 -3
  140. package/corpus/backend/no_adhoc_config_decrypt/compliant-01/modules/billing/service.py +2 -2
  141. package/corpus/backend/no_adhoc_config_decrypt/violation-01/modules/billing/service.py +2 -2
  142. package/corpus/backend/no_adhoc_logging_config/compliant-01/modules/notes/service.py +3 -3
  143. package/corpus/backend/no_adhoc_logging_config/violation-01/modules/notes/service.py +3 -3
  144. package/corpus/backend/no_adhoc_middleware/compliant-01/main.py +3 -3
  145. package/corpus/backend/no_adhoc_middleware/violation-01/main.py +4 -4
  146. package/corpus/backend/no_adhoc_middleware/violation-02/main.py +5 -5
  147. package/corpus/backend/no_adhoc_permission_literals/compliant-01/modules/notes/module.py +3 -3
  148. package/corpus/backend/no_adhoc_permission_literals/compliant-02/modules/notes/router.py +5 -5
  149. package/corpus/backend/no_adhoc_permission_literals/violation-01/expected-findings.json +7 -7
  150. package/corpus/backend/no_adhoc_permission_literals/violation-01/modules/notes/module.py +1 -1
  151. package/corpus/backend/no_adhoc_permission_literals/violation-02/expected-findings.json +7 -7
  152. package/corpus/backend/no_adhoc_permission_literals/violation-02/modules/notes/router.py +2 -2
  153. package/corpus/backend/no_adhoc_permission_literals/violation-03/expected-findings.json +12 -12
  154. package/corpus/backend/no_adhoc_permission_literals/violation-03/modules/notes/module.py +10 -10
  155. package/corpus/backend/no_app_instantiation/compliant-01/main.py +3 -3
  156. package/corpus/backend/no_app_instantiation/violation-01/main.py +3 -3
  157. package/corpus/backend/no_blocking_sleep/compliant-01/modules/notes/service.py +2 -2
  158. package/corpus/backend/no_blocking_sleep/violation-01/modules/notes/service.py +5 -5
  159. package/corpus/backend/no_cross_module_imports/compliant-01/modules/a/service.py +1 -1
  160. package/corpus/backend/no_cross_module_imports/violation-01/modules/a/service.py +1 -1
  161. package/corpus/backend/no_cross_module_imports/violation-02/modules/a/service.py +1 -1
  162. package/corpus/backend/no_dependency_overrides/compliant-01/main.py +12 -12
  163. package/corpus/backend/no_dependency_overrides/violation-01/main.py +11 -11
  164. package/corpus/backend/no_destructive_migrations/compliant-01/modules/notes/migrations/versions/0001_change.py +3 -3
  165. package/corpus/backend/no_destructive_migrations/compliant-02/modules/notes/migrations/versions/0001_change.py +8 -8
  166. package/corpus/backend/no_destructive_migrations/violation-01/expected-findings.json +7 -7
  167. package/corpus/backend/no_destructive_migrations/violation-01/modules/notes/migrations/versions/0001_change.py +2 -2
  168. package/corpus/backend/no_destructive_migrations/violation-02/expected-findings.json +7 -7
  169. package/corpus/backend/no_destructive_migrations/violation-02/modules/notes/migrations/versions/0001_change.py +2 -2
  170. package/corpus/backend/no_destructive_migrations/violation-03/expected-findings.json +7 -7
  171. package/corpus/backend/no_destructive_migrations/violation-03/modules/notes/migrations/versions/0001_change.py +3 -3
  172. package/corpus/backend/no_destructive_migrations/violation-04/expected-findings.json +12 -12
  173. package/corpus/backend/no_destructive_migrations/violation-04/modules/notes/migrations/versions/0001_change.py +6 -6
  174. package/corpus/backend/no_destructive_migrations/violation-05/expected-findings.json +12 -12
  175. package/corpus/backend/no_destructive_migrations/violation-05/modules/notes/migrations/versions/0001_change.py +3 -3
  176. package/corpus/backend/no_dynamic_sql/compliant-01/modules/notes/service.py +1 -1
  177. package/corpus/backend/no_dynamic_sql/compliant-02/modules/notes/service.py +20 -20
  178. package/corpus/backend/no_dynamic_sql/violation-01/modules/notes/service.py +2 -2
  179. package/corpus/backend/no_dynamic_sql/violation-02/modules/notes/service.py +2 -2
  180. package/corpus/backend/no_dynamic_sql/violation-03/modules/notes/service.py +7 -7
  181. package/corpus/backend/no_dynamic_sql/violation-04/modules/notes/service.py +16 -16
  182. package/corpus/backend/no_empty_tests/compliant-01/tests/test_notes.py +2 -2
  183. package/corpus/backend/no_empty_tests/violation-01/tests/test_notes.py +2 -2
  184. package/corpus/backend/no_eval_or_exec/compliant-01/modules/notes/service.py +2 -2
  185. package/corpus/backend/no_eval_or_exec/violation-01/modules/notes/service.py +2 -2
  186. package/corpus/backend/no_hardcoded_credentials/compliant-01/modules/billing/service.py +3 -3
  187. package/corpus/backend/no_hardcoded_credentials/compliant-02/modules/billing/service.py +18 -18
  188. package/corpus/backend/no_hardcoded_credentials/violation-01/expected-findings.json +7 -7
  189. package/corpus/backend/no_hardcoded_credentials/violation-01/modules/billing/service.py +2 -2
  190. package/corpus/backend/no_hardcoded_credentials/violation-02/expected-findings.json +7 -7
  191. package/corpus/backend/no_hardcoded_credentials/violation-02/modules/billing/service.py +1 -1
  192. package/corpus/backend/no_hardcoded_credentials/violation-03/expected-findings.json +22 -22
  193. package/corpus/backend/no_hardcoded_credentials/violation-03/modules/billing/service.py +11 -11
  194. package/corpus/backend/no_hardcoded_credentials/violation-04/expected-findings.json +12 -12
  195. package/corpus/backend/no_hardcoded_credentials/violation-04/modules/billing/service.py +8 -8
  196. package/corpus/backend/no_hardcoded_credentials/violation-05/expected-findings.json +12 -12
  197. package/corpus/backend/no_hardcoded_credentials/violation-05/modules/billing/service.py +4 -4
  198. package/corpus/backend/no_internal_imports/compliant-01/modules/notes/service.py +1 -1
  199. package/corpus/backend/no_internal_imports/violation-01/modules/notes/service.py +1 -1
  200. package/corpus/backend/no_manual_actor_stamping/compliant-01/modules/notes/service.py +5 -5
  201. package/corpus/backend/no_manual_actor_stamping/compliant-02/modules/notes/schemas.py +12 -12
  202. package/corpus/backend/no_manual_actor_stamping/violation-01/modules/notes/service.py +3 -3
  203. package/corpus/backend/no_manual_actor_stamping/violation-02/expected-findings.json +7 -7
  204. package/corpus/backend/no_manual_actor_stamping/violation-02/modules/notes/service.py +4 -4
  205. package/corpus/backend/no_manual_ownership_checks/compliant-01/modules/journals/service.py +3 -3
  206. package/corpus/backend/no_manual_ownership_checks/compliant-02/modules/journals/schemas.py +11 -11
  207. package/corpus/backend/no_manual_ownership_checks/violation-01/modules/journals/service.py +3 -3
  208. package/corpus/backend/no_manual_ownership_checks/violation-02/expected-findings.json +7 -7
  209. package/corpus/backend/no_manual_ownership_checks/violation-02/modules/journals/service.py +9 -9
  210. package/corpus/backend/no_manual_ownership_checks/violation-03/expected-findings.json +7 -7
  211. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/jobs.py +1 -1
  212. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/models.py +7 -7
  213. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/module.py +11 -11
  214. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/service.py +7 -7
  215. package/corpus/backend/no_manual_scope_filtering/compliant-01/modules/notes/service.py +8 -8
  216. package/corpus/backend/no_manual_scope_filtering/compliant-02/modules/notes/schemas.py +12 -12
  217. package/corpus/backend/no_manual_scope_filtering/violation-01/modules/notes/service.py +2 -2
  218. package/corpus/backend/no_manual_scope_filtering/violation-02/modules/notes/service.py +2 -2
  219. package/corpus/backend/no_manual_scope_filtering/violation-03/expected-findings.json +7 -7
  220. package/corpus/backend/no_manual_scope_filtering/violation-03/modules/notes/service.py +7 -7
  221. package/corpus/backend/no_manual_table_schema/compliant-01/modules/notes/models.py +6 -6
  222. package/corpus/backend/no_manual_table_schema/violation-01/modules/notes/models.py +8 -8
  223. package/corpus/backend/no_manual_version_assignment/compliant-01/modules/notes/service.py +5 -5
  224. package/corpus/backend/no_manual_version_assignment/violation-01/modules/notes/service.py +3 -3
  225. package/corpus/backend/no_manual_version_assignment/violation-02/modules/notes/service.py +3 -3
  226. package/corpus/backend/no_manual_version_assignment/violation-03/modules/notes/service.py +3 -3
  227. package/corpus/backend/no_mutable_default_args/compliant-01/modules/notes/service.py +3 -3
  228. package/corpus/backend/no_mutable_default_args/violation-01/modules/notes/service.py +2 -2
  229. package/corpus/backend/no_naive_datetime/compliant-01/modules/notes/service.py +2 -2
  230. package/corpus/backend/no_naive_datetime/violation-01/modules/notes/service.py +2 -2
  231. package/corpus/backend/no_naive_datetime/violation-02/modules/notes/service.py +2 -2
  232. package/corpus/backend/no_oversized_python_files/compliant-01/modules/notes/service.py +9 -9
  233. package/corpus/backend/no_oversized_python_files/violation-01/modules/notes/service.py +530 -530
  234. package/corpus/backend/no_print/compliant-01/modules/notes/service.py +7 -7
  235. package/corpus/backend/no_print/violation-01/modules/notes/service.py +2 -2
  236. package/corpus/backend/no_raw_app_routes/compliant-01/main.py +12 -12
  237. package/corpus/backend/no_raw_app_routes/violation-01/main.py +10 -10
  238. package/corpus/backend/no_raw_app_routes/violation-02/main.py +16 -16
  239. package/corpus/backend/no_raw_app_routes/violation-03/main.py +12 -12
  240. package/corpus/backend/no_raw_app_routes/violation-04/main.py +10 -10
  241. package/corpus/backend/no_raw_app_routes/violation-05/main.py +12 -12
  242. package/corpus/backend/no_raw_app_routes/violation-06/main.py +10 -10
  243. package/corpus/backend/no_raw_connection_access/compliant-01/modules/notes/service.py +5 -5
  244. package/corpus/backend/no_raw_connection_access/violation-01/modules/notes/service.py +2 -2
  245. package/corpus/backend/no_raw_connection_access/violation-02/modules/notes/service.py +2 -2
  246. package/corpus/backend/no_raw_file_references/compliant-01/modules/notes/models.py +10 -10
  247. package/corpus/backend/no_raw_file_references/violation-01/modules/notes/models.py +9 -9
  248. package/corpus/backend/no_raw_outbound_http/compliant-01/modules/notes/service.py +2 -2
  249. package/corpus/backend/no_raw_outbound_http/compliant-03/modules/notes/service.py +12 -12
  250. package/corpus/backend/no_raw_outbound_http/violation-01/expected-findings.json +7 -7
  251. package/corpus/backend/no_raw_outbound_http/violation-01/modules/notes/service.py +1 -1
  252. package/corpus/backend/no_raw_outbound_http/violation-02/expected-findings.json +7 -7
  253. package/corpus/backend/no_raw_outbound_http/violation-02/modules/notes/service.py +1 -1
  254. package/corpus/backend/no_raw_outbound_http/violation-03/expected-findings.json +7 -7
  255. package/corpus/backend/no_raw_outbound_http/violation-03/modules/notes/service.py +1 -1
  256. package/corpus/backend/no_raw_outbound_http/violation-04/expected-findings.json +12 -12
  257. package/corpus/backend/no_raw_outbound_http/violation-04/modules/notes/service.py +7 -7
  258. package/corpus/backend/no_raw_outbound_http/violation-05/expected-findings.json +12 -12
  259. package/corpus/backend/no_raw_outbound_http/violation-05/modules/notes/service.py +5 -5
  260. package/corpus/backend/no_raw_outbound_http/violation-06/expected-findings.json +12 -12
  261. package/corpus/backend/no_raw_outbound_http/violation-06/modules/notes/service.py +2 -2
  262. package/corpus/backend/no_raw_session_construction/compliant-01/modules/notes/service.py +3 -3
  263. package/corpus/backend/no_raw_session_construction/violation-01/modules/notes/service.py +3 -3
  264. package/corpus/backend/no_star_imports/compliant-01/modules/notes/service.py +1 -1
  265. package/corpus/backend/no_star_imports/violation-01/modules/notes/service.py +1 -1
  266. package/corpus/backend/no_todo_fixme/compliant-01/modules/notes/service.py +3 -3
  267. package/corpus/backend/no_todo_fixme/violation-01/modules/notes/service.py +3 -3
  268. package/corpus/backend/no_unique_columns_on_soft_delete_models/compliant-01/modules/notes/models.py +17 -17
  269. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/expected-findings.json +7 -7
  270. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/modules/notes/models.py +6 -6
  271. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/expected-findings.json +7 -7
  272. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/modules/notes/models.py +13 -13
  273. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/expected-findings.json +7 -7
  274. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/modules/notes/models.py +16 -16
  275. package/corpus/backend/offset_queries_declare_ordering/compliant-01/modules/notes/service.py +4 -4
  276. package/corpus/backend/offset_queries_declare_ordering/violation-01/modules/notes/service.py +2 -2
  277. package/corpus/backend/path_id_params_are_uuid/compliant-01/modules/notes/router.py +6 -6
  278. package/corpus/backend/path_id_params_are_uuid/violation-01/modules/notes/router.py +6 -6
  279. package/corpus/backend/policy_refs_resolve/compliant-01/modules/notes/module.py +7 -7
  280. package/corpus/backend/policy_refs_resolve/violation-01/modules/notes/module.py +8 -8
  281. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/module.py +7 -7
  282. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/router.py +3 -3
  283. package/corpus/backend/public_modules_are_read_only/violation-01/expected-findings.json +7 -7
  284. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/module.py +7 -7
  285. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/router.py +3 -3
  286. package/corpus/backend/public_modules_are_read_only/violation-02/expected-findings.json +7 -7
  287. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/module.py +7 -7
  288. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/router.py +5 -5
  289. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/models.py +6 -6
  290. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/service.py +8 -8
  291. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/models.py +9 -9
  292. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/service.py +9 -9
  293. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/models.py +6 -6
  294. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/service.py +9 -9
  295. package/corpus/backend/reads_use_base_query/violation-02/expected-findings.json +7 -7
  296. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/models.py +6 -6
  297. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/service.py +8 -8
  298. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/models.py +6 -6
  299. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/router.py +3 -3
  300. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/schemas.py +5 -5
  301. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/models.py +6 -6
  302. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/router.py +3 -3
  303. package/corpus/backend/routes_declare_response_model/compliant-01/modules/notes/router.py +8 -8
  304. package/corpus/backend/routes_declare_response_model/violation-01/modules/notes/router.py +3 -3
  305. package/corpus/backend/safe_methods_are_read_only/compliant-01/modules/notes/router.py +6 -6
  306. package/corpus/backend/safe_methods_are_read_only/compliant-02/modules/notes/router.py +5 -5
  307. package/corpus/backend/safe_methods_are_read_only/violation-01/expected-findings.json +7 -7
  308. package/corpus/backend/safe_methods_are_read_only/violation-01/modules/notes/router.py +3 -3
  309. package/corpus/backend/safe_methods_are_read_only/violation-02/expected-findings.json +7 -7
  310. package/corpus/backend/safe_methods_are_read_only/violation-02/modules/notes/router.py +5 -5
  311. package/corpus/backend/safe_methods_are_read_only/violation-03/expected-findings.json +7 -7
  312. package/corpus/backend/safe_methods_are_read_only/violation-03/modules/notes/router.py +3 -3
  313. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-01/modules/users/schemas.py +5 -5
  314. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-02/modules/accounts/schemas.py +12 -12
  315. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/expected-findings.json +7 -7
  316. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/modules/users/schemas.py +3 -3
  317. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/expected-findings.json +7 -7
  318. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/modules/connectors/router.py +8 -8
  319. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/expected-findings.json +17 -17
  320. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/modules/integrations/schemas.py +7 -7
  321. package/corpus/backend/session_imported_from_sqlmodel/compliant-01/modules/notes/service.py +5 -5
  322. package/corpus/backend/session_imported_from_sqlmodel/violation-01/modules/notes/service.py +5 -5
  323. package/corpus/backend/table_models_use_base_table/compliant-01/modules/notes/models.py +6 -6
  324. package/corpus/backend/table_models_use_base_table/violation-01/modules/notes/models.py +5 -5
  325. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/migrations/versions/0a1b2c3d4e5f_create_notes_tables.py +22 -22
  326. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/models.py +6 -6
  327. package/corpus/backend/tables_have_migrations/compliant-02/capabilities/ledger/models.py +6 -6
  328. package/corpus/backend/tables_have_migrations/violation-01/expected-findings.json +7 -7
  329. package/corpus/backend/tables_have_migrations/violation-01/modules/notes/models.py +6 -6
  330. package/corpus/backend/tables_have_migrations/violation-02/expected-findings.json +7 -7
  331. package/corpus/backend/tables_have_migrations/violation-02/modules/notes/models.py +10 -10
  332. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/models.py +6 -6
  333. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/service.py +5 -5
  334. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/models.py +8 -8
  335. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/service.py +5 -5
  336. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/models.py +6 -6
  337. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/service.py +5 -5
  338. package/corpus/backend/ungoverned_escape_hatch/compliant-01/modules/notes/service.py +5 -5
  339. package/corpus/backend/ungoverned_escape_hatch/compliant-02/modules/notes/service.py +4 -4
  340. package/corpus/backend/ungoverned_escape_hatch/violation-01/modules/notes/service.py +2 -2
  341. package/corpus/backend/update_schemas_inherit_base_update_schema/compliant-01/modules/notes/schemas.py +2 -2
  342. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-01/modules/notes/schemas.py +2 -2
  343. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-02/modules/notes/schemas.py +5 -5
  344. package/corpus/frontend/escape-hatch/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  345. package/corpus/frontend/escape-hatch/violation-01/src/modules/widgets/Widget.tsx +4 -4
  346. package/corpus/frontend/escape-hatch/violation-02/src/modules/widgets/Widget.tsx +7 -7
  347. package/corpus/frontend/escape-hatch/violation-03/src/modules/widgets/Widget.tsx +7 -7
  348. package/corpus/frontend/generated-client-only/compliant-01/src/modules/widgets/Widget.tsx +6 -6
  349. package/corpus/frontend/generated-client-only/compliant-02/src/modules/widgets/Widget.tsx +21 -21
  350. package/corpus/frontend/generated-client-only/compliant-03/src/modules/widgets/Widget.tsx +7 -7
  351. package/corpus/frontend/generated-client-only/compliant-04/src/modules/widgets/Widget.tsx +17 -17
  352. package/corpus/frontend/generated-client-only/violation-01/src/modules/widgets/Widget.tsx +3 -3
  353. package/corpus/frontend/generated-client-only/violation-02/src/modules/widgets/Widget.tsx +9 -9
  354. package/corpus/frontend/generated-client-only/violation-03/src/modules/widgets/Widget.tsx +14 -14
  355. package/corpus/frontend/generated-client-only/violation-04/src/modules/widgets/Widget.tsx +6 -6
  356. package/corpus/frontend/layout-contract/compliant-01/layout-contract.json +3 -3
  357. package/corpus/frontend/layout-contract/compliant-01/src/modules/widgets/Widget.tsx +8 -8
  358. package/corpus/frontend/layout-contract/violation-01/layout-contract.json +3 -3
  359. package/corpus/frontend/layout-contract/violation-01/src/modules/widgets/Widget.tsx +8 -8
  360. package/corpus/frontend/no-cross-module-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  361. package/corpus/frontend/no-cross-module-imports/violation-01/src/modules/widgets/Widget.tsx +3 -3
  362. package/corpus/frontend/no-deep-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  363. package/corpus/frontend/no-deep-imports/violation-01/src/modules/widgets/Widget.tsx +2 -2
  364. package/corpus/frontend/no-dom-html-injection/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  365. package/corpus/frontend/no-dom-html-injection/compliant-02/src/modules/widgets/Widget.tsx +11 -11
  366. package/corpus/frontend/no-dom-html-injection/violation-01/src/modules/widgets/Widget.tsx +3 -3
  367. package/corpus/frontend/no-dom-html-injection/violation-02/src/modules/widgets/Widget.tsx +7 -7
  368. package/corpus/frontend/no-dom-html-injection/violation-03/src/modules/widgets/Widget.tsx +12 -12
  369. package/corpus/frontend/no-eval/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  370. package/corpus/frontend/no-eval/compliant-02/src/modules/widgets/Widget.tsx +11 -11
  371. package/corpus/frontend/no-eval/violation-01/src/modules/widgets/Widget.tsx +3 -3
  372. package/corpus/frontend/no-eval/violation-02/src/modules/widgets/Widget.tsx +7 -7
  373. package/corpus/frontend/no-eval/violation-03/src/modules/widgets/Widget.tsx +10 -10
  374. package/corpus/frontend/no-eval/violation-04/src/modules/widgets/Widget.tsx +6 -6
  375. package/corpus/frontend/no-inline-styling/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  376. package/corpus/frontend/no-inline-styling/violation-01/src/modules/widgets/Widget.tsx +3 -3
  377. package/corpus/frontend/no-inline-styling/violation-02/src/modules/widgets/Widget.tsx +6 -6
  378. package/corpus/frontend/no-style-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  379. package/corpus/frontend/no-style-imports/violation-01/src/modules/widgets/Widget.tsx +2 -2
  380. package/corpus/frontend/no-unsafe-href/compliant-01/src/modules/widgets/Widget.tsx +3 -3
  381. package/corpus/frontend/no-unsafe-href/violation-01/src/modules/widgets/Widget.tsx +3 -3
  382. package/corpus/frontend/no-unsafe-target-blank/compliant-01/src/modules/widgets/Widget.tsx +3 -3
  383. package/corpus/frontend/no-unsafe-target-blank/violation-01/src/modules/widgets/Widget.tsx +3 -3
  384. package/corpus/frontend/router-links/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  385. package/corpus/frontend/router-links/violation-01/src/modules/widgets/Widget.tsx +3 -3
  386. package/corpus/frontend/token-styled-elements/compliant-01/src/modules/widgets/Widget.tsx +4 -4
  387. package/corpus/frontend/token-styled-elements/compliant-02/src/modules/widgets/Widget.tsx +4 -4
  388. package/corpus/frontend/token-styled-elements/violation-01/src/modules/widgets/Widget.tsx +3 -3
  389. package/findings.schema.json +40 -40
  390. package/package.json +23 -23
  391. package/restricted-surface.json +10 -10
  392. package/scorecard.schema.json +52 -52
@@ -1,26 +1,26 @@
1
- {
2
- "id": "frontend/generated-client-only",
3
- "surface": "frontend",
4
- "title": "All network egress goes through the generated, typed client",
5
- "intent": "Raw fetch / XMLHttpRequest / WebSocket / EventSource (and navigator.sendBeacon) bypass the app's single audited, typed egress path. One client means one place for auth, errors, and contract types. The refused globals and member calls are declared in restricted-surface.json (restrictedGlobals, restrictedMemberCalls).",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "BOUNDARY_SPEC.restrictedGlobals",
12
- "reported_as": "no-restricted-globals"
13
- }
14
- ],
15
- "reference": "useTerpClient() + unwrap from @terp/react-core for request/response; useRealtimeChannel() from @terp/react-core for typed SSE/WebSocket subscriptions (one-use ticket minted through the generated client).",
16
- "opt_out": "// terp-allow-generated-client-only: <reason>",
17
- "runtime": {
18
- "applicability": "not-applicable",
19
- "rationale": "In the running bundle the sanctioned generated client is itself built on fetch, so egress-primitive use cannot be attributed to an app module from inside the app. One typed egress path is an authoring contract; network policy beyond it (CSP connect-src) is deployment configuration the framework does not own per module."
20
- },
21
- "restricted_surface": [
22
- "restrictedGlobals",
23
- "restrictedMemberCalls"
24
- ],
25
- "corpus": true
26
- }
1
+ {
2
+ "id": "frontend/generated-client-only",
3
+ "surface": "frontend",
4
+ "title": "All network egress goes through the generated, typed client",
5
+ "intent": "Raw fetch / XMLHttpRequest / WebSocket / EventSource (and navigator.sendBeacon) bypass the app's single audited, typed egress path. One client means one place for auth, errors, and contract types. The refused globals and member calls are declared in restricted-surface.json (restrictedGlobals, restrictedMemberCalls).",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "BOUNDARY_SPEC.restrictedGlobals",
12
+ "reported_as": "no-restricted-globals"
13
+ }
14
+ ],
15
+ "reference": "useTerpClient() + unwrap from @terpjs/react-core for request/response; useRealtimeChannel() from @terpjs/react-core for typed SSE/WebSocket subscriptions (one-use ticket minted through the generated client).",
16
+ "opt_out": "// terp-allow-generated-client-only: <reason>",
17
+ "runtime": {
18
+ "applicability": "not-applicable",
19
+ "rationale": "In the running bundle the sanctioned generated client is itself built on fetch, so egress-primitive use cannot be attributed to an app module from inside the app. One typed egress path is an authoring contract; network policy beyond it (CSP connect-src) is deployment configuration the framework does not own per module."
20
+ },
21
+ "restricted_surface": [
22
+ "restrictedGlobals",
23
+ "restrictedMemberCalls"
24
+ ],
25
+ "corpus": true
26
+ }
@@ -1,26 +1,26 @@
1
- {
2
- "id": "frontend/layout-contract",
3
- "surface": "frontend",
4
- "title": "An opted-in app's archetype body slots accept only the contract's components",
5
- "intent": "With a checked-in layout contract, each page archetype's body slot is constrained to the contract's sanctioned components, so screens stay structurally consistent and the failure message tells an agent exactly how to build the screen. Paired with the fail-closed runtime check on the rendered slot.",
6
- "layer": "static-bespoke",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "terp/layout-contract",
12
- "reported_as": "terp/layout-contract"
13
- },
14
- {
15
- "kind": "runtime",
16
- "tool": "@terp/react-core",
17
- "ref": "verifySlotChildren"
18
- }
19
- ],
20
- "opt_out": "// terp-allow-layout-contract: <reason>",
21
- "reference": "frontend/layout-contract.json + the layoutContract bootstrap option (ADR 0079); verifySlotChildren in @terp/react-core is the runtime DOM check.",
22
- "runtime": {
23
- "applicability": "required"
24
- },
25
- "corpus": true
26
- }
1
+ {
2
+ "id": "frontend/layout-contract",
3
+ "surface": "frontend",
4
+ "title": "An opted-in app's archetype body slots accept only the contract's components",
5
+ "intent": "With a checked-in layout contract, each page archetype's body slot is constrained to the contract's sanctioned components, so screens stay structurally consistent and the failure message tells an agent exactly how to build the screen. Paired with the fail-closed runtime check on the rendered slot.",
6
+ "layer": "static-bespoke",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "terp/layout-contract",
12
+ "reported_as": "terp/layout-contract"
13
+ },
14
+ {
15
+ "kind": "runtime",
16
+ "tool": "@terpjs/react-core",
17
+ "ref": "verifySlotChildren"
18
+ }
19
+ ],
20
+ "opt_out": "// terp-allow-layout-contract: <reason>",
21
+ "reference": "frontend/layout-contract.json + the layoutContract bootstrap option (ADR 0079); verifySlotChildren in @terpjs/react-core is the runtime DOM check.",
22
+ "runtime": {
23
+ "applicability": "required"
24
+ },
25
+ "corpus": true
26
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "id": "frontend/no-cross-module-imports",
3
- "surface": "frontend",
4
- "title": "An app module never imports a sibling module",
5
- "intent": "Modules couple only through the platform packages and their own files. A sibling import — static, dynamic import(), relative or via the app alias — creates hidden coupling that breaks module isolation and independent evolution.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "terp/no-cross-module-imports",
12
- "reported_as": "terp/no-cross-module-imports"
13
- }
14
- ],
15
- "opt_out": "// terp-allow-no-cross-module-imports: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "Module independence is an import-graph property of the source tree; the bundler merges every module into one dependency graph, after which a cross-module edge is indistinguishable from composition."
19
- },
20
- "corpus": true
21
- }
1
+ {
2
+ "id": "frontend/no-cross-module-imports",
3
+ "surface": "frontend",
4
+ "title": "An app module never imports a sibling module",
5
+ "intent": "Modules couple only through the platform packages and their own files. A sibling import — static, dynamic import(), relative or via the app alias — creates hidden coupling that breaks module isolation and independent evolution.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "terp/no-cross-module-imports",
12
+ "reported_as": "terp/no-cross-module-imports"
13
+ }
14
+ ],
15
+ "opt_out": "// terp-allow-no-cross-module-imports: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "Module independence is an import-graph property of the source tree; the bundler merges every module into one dependency graph, after which a cross-module edge is indistinguishable from composition."
19
+ },
20
+ "corpus": true
21
+ }
@@ -1,25 +1,25 @@
1
- {
2
- "id": "frontend/no-deep-imports",
3
- "surface": "frontend",
4
- "title": "App modules import platform packages from the root only",
5
- "intent": "A deep import into a platform package's internals couples the app to files that are free to move; the package root is the published, stable surface. The refused path segments are declared in restricted-surface.json (deepImportPathSegments).",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "BOUNDARY_SPEC.internalImportPatterns",
12
- "reported_as": "no-restricted-imports"
13
- }
14
- ],
15
- "reference": "@terp/*/src/* and @terp/*/dist/* imports are refused; import from the @terp/* package root.",
16
- "opt_out": "// terp-allow-no-deep-imports: <reason>",
17
- "runtime": {
18
- "applicability": "not-applicable",
19
- "rationale": "Package-boundary form: the bundler resolves and inlines whatever was imported, and the built artifact retains no distinction between a root import and a deep one."
20
- },
21
- "restricted_surface": [
22
- "deepImportPathSegments"
23
- ],
24
- "corpus": true
25
- }
1
+ {
2
+ "id": "frontend/no-deep-imports",
3
+ "surface": "frontend",
4
+ "title": "App modules import platform packages from the root only",
5
+ "intent": "A deep import into a platform package's internals couples the app to files that are free to move; the package root is the published, stable surface. The refused path segments are declared in restricted-surface.json (deepImportPathSegments).",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "BOUNDARY_SPEC.internalImportPatterns",
12
+ "reported_as": "no-restricted-imports"
13
+ }
14
+ ],
15
+ "reference": "@terpjs/*/src/* and @terpjs/*/dist/* imports are refused; import from the @terpjs/* package root.",
16
+ "opt_out": "// terp-allow-no-deep-imports: <reason>",
17
+ "runtime": {
18
+ "applicability": "not-applicable",
19
+ "rationale": "Package-boundary form: the bundler resolves and inlines whatever was imported, and the built artifact retains no distinction between a root import and a deep one."
20
+ },
21
+ "restricted_surface": [
22
+ "deepImportPathSegments"
23
+ ],
24
+ "corpus": true
25
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "frontend/no-dom-html-injection",
3
- "surface": "frontend",
4
- "title": "DOM HTML-injection sinks are refused in app modules",
5
- "intent": "dangerouslySetInnerHTML, innerHTML/outerHTML assignment, insertAdjacentHTML and document.write inject markup that bypasses the framework's output escaping — the canonical XSS sink. Rich text renders through the stack's sanctioned rich-text component instead.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "terp/no-dom-html-injection",
12
- "reported_as": "terp/no-dom-html-injection"
13
- }
14
- ],
15
- "reference": "The Markdown component from @terp/react-core for rich text.",
16
- "opt_out": "// terp-allow-no-dom-html-injection: <reason>",
17
- "runtime": {
18
- "applicability": "not-applicable",
19
- "rationale": "The sinks are platform primitives on every element; React's default output escaping is the ambient constructive protection and the sanctioned Markdown component sanitises rich text. A runtime hook over every element's HTML mutation is not a seam react-core owns or could make sound -- vendored code legitimately uses the same primitives."
20
- },
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "frontend/no-dom-html-injection",
3
+ "surface": "frontend",
4
+ "title": "DOM HTML-injection sinks are refused in app modules",
5
+ "intent": "dangerouslySetInnerHTML, innerHTML/outerHTML assignment, insertAdjacentHTML and document.write inject markup that bypasses the framework's output escaping — the canonical XSS sink. Rich text renders through the stack's sanctioned rich-text component instead.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "terp/no-dom-html-injection",
12
+ "reported_as": "terp/no-dom-html-injection"
13
+ }
14
+ ],
15
+ "reference": "The Markdown component from @terpjs/react-core for rich text.",
16
+ "opt_out": "// terp-allow-no-dom-html-injection: <reason>",
17
+ "runtime": {
18
+ "applicability": "not-applicable",
19
+ "rationale": "The sinks are platform primitives on every element; React's default output escaping is the ambient constructive protection and the sanctioned Markdown component sanitises rich text. A runtime hook over every element's HTML mutation is not a seam react-core owns or could make sound -- vendored code legitimately uses the same primitives."
20
+ },
21
+ "corpus": true
22
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "id": "frontend/no-eval",
3
- "surface": "frontend",
4
- "title": "eval() and new Function() are refused in app modules",
5
- "intent": "Runtime code evaluation defeats every static guarantee the boundary provides and is a classic injection primitive. There is no sanctioned dynamic-code path in an app module.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "terp/no-eval",
12
- "reported_as": "terp/no-eval"
13
- }
14
- ],
15
- "opt_out": "// terp-allow-no-eval: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The prohibition is on authoring dynamic-code primitives in app modules \u2014 a source-form fact. In the running bundle the primitive itself is a platform global the framework cannot remove, and legitimately vendored code may reference it, so a runtime hook cannot attribute authorship to an app module. Deployment-level CSP can harden the served app, but it is deployment configuration, not a control this framework owns per rule."
19
- },
20
- "corpus": true
21
- }
1
+ {
2
+ "id": "frontend/no-eval",
3
+ "surface": "frontend",
4
+ "title": "eval() and new Function() are refused in app modules",
5
+ "intent": "Runtime code evaluation defeats every static guarantee the boundary provides and is a classic injection primitive. There is no sanctioned dynamic-code path in an app module.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "terp/no-eval",
12
+ "reported_as": "terp/no-eval"
13
+ }
14
+ ],
15
+ "opt_out": "// terp-allow-no-eval: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The prohibition is on authoring dynamic-code primitives in app modules \u2014 a source-form fact. In the running bundle the primitive itself is a platform global the framework cannot remove, and legitimately vendored code may reference it, so a runtime hook cannot attribute authorship to an app module. Deployment-level CSP can harden the served app, but it is deployment configuration, not a control this framework owns per rule."
19
+ },
20
+ "corpus": true
21
+ }
@@ -1,25 +1,25 @@
1
- {
2
- "id": "frontend/no-inline-styling",
3
- "surface": "frontend",
4
- "title": "style and className attributes are refused in app modules",
5
- "intent": "A style or className attribute is a side channel into hand-authored CSS that drifts per screen; styling flows from the app's design tokens and sanctioned layout primitives only. The refused attributes are declared in restricted-surface.json (restrictedAttributes).",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "BOUNDARY_SPEC.restrictedAttributes",
12
- "reported_as": "no-restricted-syntax"
13
- }
14
- ],
15
- "reference": "Layout via Stack, DetailList and the page archetypes from @terp/react-core; theming via the app's token source.",
16
- "opt_out": "// terp-allow-no-inline-styling: <reason>",
17
- "runtime": {
18
- "applicability": "not-applicable",
19
- "rationale": "style/className are authoring-surface attributes: the token pipeline itself emits inline styles and class names into the built DOM, so their presence at runtime is legitimate and unattributable to app authorship."
20
- },
21
- "restricted_surface": [
22
- "restrictedAttributes"
23
- ],
24
- "corpus": true
25
- }
1
+ {
2
+ "id": "frontend/no-inline-styling",
3
+ "surface": "frontend",
4
+ "title": "style and className attributes are refused in app modules",
5
+ "intent": "A style or className attribute is a side channel into hand-authored CSS that drifts per screen; styling flows from the app's design tokens and sanctioned layout primitives only. The refused attributes are declared in restricted-surface.json (restrictedAttributes).",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "BOUNDARY_SPEC.restrictedAttributes",
12
+ "reported_as": "no-restricted-syntax"
13
+ }
14
+ ],
15
+ "reference": "Layout via Stack, DetailList and the page archetypes from @terpjs/react-core; theming via the app's token source.",
16
+ "opt_out": "// terp-allow-no-inline-styling: <reason>",
17
+ "runtime": {
18
+ "applicability": "not-applicable",
19
+ "rationale": "style/className are authoring-surface attributes: the token pipeline itself emits inline styles and class names into the built DOM, so their presence at runtime is legitimate and unattributable to app authorship."
20
+ },
21
+ "restricted_surface": [
22
+ "restrictedAttributes"
23
+ ],
24
+ "corpus": true
25
+ }
@@ -1,24 +1,24 @@
1
- {
2
- "id": "frontend/no-style-imports",
3
- "surface": "frontend",
4
- "title": "Module-authored stylesheets are refused",
5
- "intent": "Importing a stylesheet from an app module reintroduces per-screen CSS; theming flows from the app's token source only. The refused stylesheet extensions are declared in restricted-surface.json (styleImportExtensions).",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "BOUNDARY_SPEC.styleImportPatterns",
12
- "reported_as": "no-restricted-imports"
13
- }
14
- ],
15
- "opt_out": "// terp-allow-no-style-imports: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "A stylesheet import is a build-graph edge; the bundler compiles it into the emitted CSS, after which module authorship does not exist."
19
- },
20
- "restricted_surface": [
21
- "styleImportExtensions"
22
- ],
23
- "corpus": true
24
- }
1
+ {
2
+ "id": "frontend/no-style-imports",
3
+ "surface": "frontend",
4
+ "title": "Module-authored stylesheets are refused",
5
+ "intent": "Importing a stylesheet from an app module reintroduces per-screen CSS; theming flows from the app's token source only. The refused stylesheet extensions are declared in restricted-surface.json (styleImportExtensions).",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "BOUNDARY_SPEC.styleImportPatterns",
12
+ "reported_as": "no-restricted-imports"
13
+ }
14
+ ],
15
+ "opt_out": "// terp-allow-no-style-imports: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "A stylesheet import is a build-graph edge; the bundler compiles it into the emitted CSS, after which module authorship does not exist."
19
+ },
20
+ "restricted_surface": [
21
+ "styleImportExtensions"
22
+ ],
23
+ "corpus": true
24
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "id": "frontend/no-unsafe-href",
3
- "surface": "frontend",
4
- "title": "javascript: URLs are refused in app modules",
5
- "intent": "A javascript: href executes script on click \u2014 an XSS vector that survives the framework's attribute escaping. Links carry http(s), mailto, tel or in-app router paths only.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "terp/no-unsafe-href",
12
- "reported_as": "terp/no-unsafe-href"
13
- }
14
- ],
15
- "opt_out": "// terp-allow-no-unsafe-href: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "The refusal is of authoring javascript: URLs in source. Data-driven links are the sanctioned components' own concern (the Markdown component strips unsafe protocols at render -- its control, not this rule's), and raw anchors are refused wholesale in app modules anyway."
19
- },
20
- "corpus": true
21
- }
1
+ {
2
+ "id": "frontend/no-unsafe-href",
3
+ "surface": "frontend",
4
+ "title": "javascript: URLs are refused in app modules",
5
+ "intent": "A javascript: href executes script on click \u2014 an XSS vector that survives the framework's attribute escaping. Links carry http(s), mailto, tel or in-app router paths only.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "terp/no-unsafe-href",
12
+ "reported_as": "terp/no-unsafe-href"
13
+ }
14
+ ],
15
+ "opt_out": "// terp-allow-no-unsafe-href: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "The refusal is of authoring javascript: URLs in source. Data-driven links are the sanctioned components' own concern (the Markdown component strips unsafe protocols at render -- its control, not this rule's), and raw anchors are refused wholesale in app modules anyway."
19
+ },
20
+ "corpus": true
21
+ }
@@ -1,21 +1,21 @@
1
- {
2
- "id": "frontend/no-unsafe-target-blank",
3
- "surface": "frontend",
4
- "title": "A static target=\"_blank\" link declares rel=\"noopener\"",
5
- "intent": "Without rel=noopener the opened page holds a window.opener reference to the app \u2014 the reverse-tabnabbing primitive. The rule enforces the safe default on every statically-authored external link.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "terp/no-unsafe-target-blank",
12
- "reported_as": "terp/no-unsafe-target-blank"
13
- }
14
- ],
15
- "opt_out": "// terp-allow-no-unsafe-target-blank: <reason>",
16
- "runtime": {
17
- "applicability": "not-applicable",
18
- "rationale": "An authored static target=_blank without rel is a source form; the residual risk it addresses is legacy-engine reverse-tabnabbing, fixable only where the anchor is written. Modern engines default to noopener behaviour, and raw anchors are refused wholesale in app modules regardless."
19
- },
20
- "corpus": true
21
- }
1
+ {
2
+ "id": "frontend/no-unsafe-target-blank",
3
+ "surface": "frontend",
4
+ "title": "A static target=\"_blank\" link declares rel=\"noopener\"",
5
+ "intent": "Without rel=noopener the opened page holds a window.opener reference to the app \u2014 the reverse-tabnabbing primitive. The rule enforces the safe default on every statically-authored external link.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "terp/no-unsafe-target-blank",
12
+ "reported_as": "terp/no-unsafe-target-blank"
13
+ }
14
+ ],
15
+ "opt_out": "// terp-allow-no-unsafe-target-blank: <reason>",
16
+ "runtime": {
17
+ "applicability": "not-applicable",
18
+ "rationale": "An authored static target=_blank without rel is a source form; the residual risk it addresses is legacy-engine reverse-tabnabbing, fixable only where the anchor is written. Modern engines default to noopener behaviour, and raw anchors are refused wholesale in app modules regardless."
19
+ },
20
+ "corpus": true
21
+ }
@@ -1,22 +1,22 @@
1
- {
2
- "id": "frontend/router-links",
3
- "surface": "frontend",
4
- "title": "In-app links go through the router, never a raw anchor",
5
- "intent": "A raw <a href=\"/...\"> forces a full reload and skips the role-aware route guard; in-app navigation uses the stack's router-integrated link. External https:// anchors stay allowed.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "BOUNDARY_SPEC.restrictInAppAnchors",
12
- "reported_as": "no-restricted-syntax"
13
- }
14
- ],
15
- "reference": "Link from @terp/react-core (router-integrated, role-aware).",
16
- "opt_out": "// terp-allow-router-links: <reason>",
17
- "runtime": {
18
- "applicability": "not-applicable",
19
- "rationale": "In the rendered DOM the router's own Link IS an anchor -- raw-anchor authorship is only visible in source, so no DOM check can distinguish a sanctioned link from a hand-written one."
20
- },
21
- "corpus": true
22
- }
1
+ {
2
+ "id": "frontend/router-links",
3
+ "surface": "frontend",
4
+ "title": "In-app links go through the router, never a raw anchor",
5
+ "intent": "A raw <a href=\"/...\"> forces a full reload and skips the role-aware route guard; in-app navigation uses the stack's router-integrated link. External https:// anchors stay allowed.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "BOUNDARY_SPEC.restrictInAppAnchors",
12
+ "reported_as": "no-restricted-syntax"
13
+ }
14
+ ],
15
+ "reference": "Link from @terpjs/react-core (router-integrated, role-aware).",
16
+ "opt_out": "// terp-allow-router-links: <reason>",
17
+ "runtime": {
18
+ "applicability": "not-applicable",
19
+ "rationale": "In the rendered DOM the router's own Link IS an anchor -- raw-anchor authorship is only visible in source, so no DOM check can distinguish a sanctioned link from a hand-written one."
20
+ },
21
+ "corpus": true
22
+ }
@@ -1,25 +1,25 @@
1
- {
2
- "id": "frontend/token-styled-elements",
3
- "surface": "frontend",
4
- "title": "Raw interactive/structural HTML elements are refused in app modules",
5
- "intent": "A raw <button>/<input>/<select>/<textarea>/<table>/<dialog>/<form> bypasses the app's sanctioned, token-styled primitive surface (accessible and theme-consistent by construction). The refused elements are declared in restricted-surface.json (restrictedElements); each has a sanctioned replacement in the stack's component surface, and the violation message names it.",
6
- "layer": "static-portable",
7
- "enforcement": [
8
- {
9
- "kind": "build-time",
10
- "tool": "@terp/eslint-boundaries",
11
- "ref": "BOUNDARY_SPEC.restrictedElements",
12
- "reported_as": "no-restricted-syntax"
13
- }
14
- ],
15
- "reference": "Button, Input, Select, Textarea, DataView, ConfirmDialog and Stack as=\"form\" from @terp/react-core (BOUNDARY_SPEC.restrictedElements maps each element to its replacement).",
16
- "opt_out": "// terp-allow-token-styled-elements: <reason>",
17
- "runtime": {
18
- "applicability": "not-applicable",
19
- "rationale": "The sanctioned components themselves render the raw elements into the built DOM -- every button on screen is legitimate output of Button. Compilation erases which JSX authored a node, and third-party components legitimately render the same primitives, so a DOM scan cannot soundly attribute a raw element to app authorship (unlike the layout contract's slot check, which verifies the identity of sanctioned components at one owned seam)."
20
- },
21
- "restricted_surface": [
22
- "restrictedElements"
23
- ],
24
- "corpus": true
25
- }
1
+ {
2
+ "id": "frontend/token-styled-elements",
3
+ "surface": "frontend",
4
+ "title": "Raw interactive/structural HTML elements are refused in app modules",
5
+ "intent": "A raw <button>/<input>/<select>/<textarea>/<table>/<dialog>/<form> bypasses the app's sanctioned, token-styled primitive surface (accessible and theme-consistent by construction). The refused elements are declared in restricted-surface.json (restrictedElements); each has a sanctioned replacement in the stack's component surface, and the violation message names it.",
6
+ "layer": "static-portable",
7
+ "enforcement": [
8
+ {
9
+ "kind": "build-time",
10
+ "tool": "@terpjs/eslint-boundaries",
11
+ "ref": "BOUNDARY_SPEC.restrictedElements",
12
+ "reported_as": "no-restricted-syntax"
13
+ }
14
+ ],
15
+ "reference": "Button, Input, Select, Textarea, DataView, ConfirmDialog and Stack as=\"form\" from @terpjs/react-core (BOUNDARY_SPEC.restrictedElements maps each element to its replacement).",
16
+ "opt_out": "// terp-allow-token-styled-elements: <reason>",
17
+ "runtime": {
18
+ "applicability": "not-applicable",
19
+ "rationale": "The sanctioned components themselves render the raw elements into the built DOM -- every button on screen is legitimate output of Button. Compilation erases which JSX authored a node, and third-party components legitimately render the same primitives, so a DOM scan cannot soundly attribute a raw element to app authorship (unlike the layout contract's slot check, which verifies the identity of sanctioned components at one owned seam)."
20
+ },
21
+ "restricted_surface": [
22
+ "restrictedElements"
23
+ ],
24
+ "corpus": true
25
+ }