shield360 1.0.1 → 1.2.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 (424) hide show
  1. package/README.md +392 -37
  2. package/dist/__tests__/build-info.test.d.ts +1 -0
  3. package/dist/__tests__/build-info.test.js +196 -0
  4. package/dist/__tests__/build-info.test.js.map +1 -0
  5. package/dist/__tests__/code-location.test.d.ts +1 -0
  6. package/dist/__tests__/code-location.test.js +180 -0
  7. package/dist/__tests__/code-location.test.js.map +1 -0
  8. package/dist/__tests__/masking.test.d.ts +1 -0
  9. package/dist/__tests__/masking.test.js +66 -0
  10. package/dist/__tests__/masking.test.js.map +1 -0
  11. package/dist/__tests__/pricing-claude-5.test.js +3 -0
  12. package/dist/__tests__/pricing-claude-5.test.js.map +1 -1
  13. package/dist/__tests__/register.e2e.test.d.ts +1 -0
  14. package/dist/__tests__/register.e2e.test.js +264 -0
  15. package/dist/__tests__/register.e2e.test.js.map +1 -0
  16. package/dist/__tests__/register.test.js +60 -0
  17. package/dist/__tests__/register.test.js.map +1 -1
  18. package/dist/agents/__tests__/agents.test.d.ts +1 -0
  19. package/dist/agents/__tests__/agents.test.js +529 -0
  20. package/dist/agents/__tests__/agents.test.js.map +1 -0
  21. package/dist/agents/index.d.ts +15 -0
  22. package/dist/agents/index.js +25 -0
  23. package/dist/agents/index.js.map +1 -0
  24. package/dist/agents/passport.d.ts +92 -0
  25. package/dist/agents/passport.js +301 -0
  26. package/dist/agents/passport.js.map +1 -0
  27. package/dist/agents/policy.d.ts +24 -0
  28. package/dist/agents/policy.js +93 -0
  29. package/dist/agents/policy.js.map +1 -0
  30. package/dist/agents/replay.d.ts +20 -0
  31. package/dist/agents/replay.js +47 -0
  32. package/dist/agents/replay.js.map +1 -0
  33. package/dist/agents/report.d.ts +30 -0
  34. package/dist/agents/report.js +93 -0
  35. package/dist/agents/report.js.map +1 -0
  36. package/dist/agents/types.d.ts +148 -0
  37. package/dist/agents/types.js +37 -0
  38. package/dist/agents/types.js.map +1 -0
  39. package/dist/agents/verifier.d.ts +33 -0
  40. package/dist/agents/verifier.js +276 -0
  41. package/dist/agents/verifier.js.map +1 -0
  42. package/dist/ai-security/__tests__/bench.test.d.ts +1 -0
  43. package/dist/ai-security/__tests__/bench.test.js +50 -0
  44. package/dist/ai-security/__tests__/bench.test.js.map +1 -0
  45. package/dist/ai-security/__tests__/checks.test.d.ts +1 -0
  46. package/dist/ai-security/__tests__/checks.test.js +248 -0
  47. package/dist/ai-security/__tests__/checks.test.js.map +1 -0
  48. package/dist/ai-security/__tests__/config.test.d.ts +1 -0
  49. package/dist/ai-security/__tests__/config.test.js +164 -0
  50. package/dist/ai-security/__tests__/config.test.js.map +1 -0
  51. package/dist/ai-security/__tests__/detection.test.d.ts +1 -0
  52. package/dist/ai-security/__tests__/detection.test.js +114 -0
  53. package/dist/ai-security/__tests__/detection.test.js.map +1 -0
  54. package/dist/ai-security/__tests__/fixtures.d.ts +8 -0
  55. package/dist/ai-security/__tests__/fixtures.js +30 -0
  56. package/dist/ai-security/__tests__/fixtures.js.map +1 -0
  57. package/dist/ai-security/__tests__/hooks.test.d.ts +1 -0
  58. package/dist/ai-security/__tests__/hooks.test.js +174 -0
  59. package/dist/ai-security/__tests__/hooks.test.js.map +1 -0
  60. package/dist/ai-security/__tests__/stream.test.d.ts +1 -0
  61. package/dist/ai-security/__tests__/stream.test.js +85 -0
  62. package/dist/ai-security/__tests__/stream.test.js.map +1 -0
  63. package/dist/ai-security/config.d.ts +94 -0
  64. package/dist/ai-security/config.js +241 -0
  65. package/dist/ai-security/config.js.map +1 -0
  66. package/dist/ai-security/context.d.ts +45 -0
  67. package/dist/ai-security/context.js +108 -0
  68. package/dist/ai-security/context.js.map +1 -0
  69. package/dist/ai-security/custom-kinds.d.ts +23 -0
  70. package/dist/ai-security/custom-kinds.js +97 -0
  71. package/dist/ai-security/custom-kinds.js.map +1 -0
  72. package/dist/ai-security/events.d.ts +77 -0
  73. package/dist/ai-security/events.js +153 -0
  74. package/dist/ai-security/events.js.map +1 -0
  75. package/dist/ai-security/hooks.d.ts +48 -0
  76. package/dist/ai-security/hooks.js +461 -0
  77. package/dist/ai-security/hooks.js.map +1 -0
  78. package/dist/ai-security/index.d.ts +209 -0
  79. package/dist/ai-security/index.js +587 -0
  80. package/dist/ai-security/index.js.map +1 -0
  81. package/dist/ai-security/injection-rules.json +1024 -0
  82. package/dist/ai-security/injection.d.ts +46 -0
  83. package/dist/ai-security/injection.js +635 -0
  84. package/dist/ai-security/injection.js.map +1 -0
  85. package/dist/ai-security/jailbreak.d.ts +38 -0
  86. package/dist/ai-security/jailbreak.js +73 -0
  87. package/dist/ai-security/jailbreak.js.map +1 -0
  88. package/dist/ai-security/limits.d.ts +84 -0
  89. package/dist/ai-security/limits.js +195 -0
  90. package/dist/ai-security/limits.js.map +1 -0
  91. package/dist/ai-security/mcp.d.ts +82 -0
  92. package/dist/ai-security/mcp.js +150 -0
  93. package/dist/ai-security/mcp.js.map +1 -0
  94. package/dist/ai-security/output.d.ts +107 -0
  95. package/dist/ai-security/output.js +390 -0
  96. package/dist/ai-security/output.js.map +1 -0
  97. package/dist/ai-security/policy.d.ts +125 -0
  98. package/dist/ai-security/policy.js +196 -0
  99. package/dist/ai-security/policy.js.map +1 -0
  100. package/dist/ai-security/rag.d.ts +70 -0
  101. package/dist/ai-security/rag.js +174 -0
  102. package/dist/ai-security/rag.js.map +1 -0
  103. package/dist/ai-security/tool-rules.json +203 -0
  104. package/dist/ai-security/tools.d.ts +46 -0
  105. package/dist/ai-security/tools.js +62 -0
  106. package/dist/ai-security/tools.js.map +1 -0
  107. package/dist/budgets/__tests__/budgets.test.d.ts +1 -0
  108. package/dist/budgets/__tests__/budgets.test.js +134 -0
  109. package/dist/budgets/__tests__/budgets.test.js.map +1 -0
  110. package/dist/budgets/auto.d.ts +25 -0
  111. package/dist/budgets/auto.js +63 -0
  112. package/dist/budgets/auto.js.map +1 -0
  113. package/dist/budgets/index.d.ts +110 -0
  114. package/dist/budgets/index.js +239 -0
  115. package/dist/budgets/index.js.map +1 -0
  116. package/dist/config.js +1 -1
  117. package/dist/config.js.map +1 -1
  118. package/dist/customer/__tests__/customer.test.d.ts +1 -0
  119. package/dist/customer/__tests__/customer.test.js +155 -0
  120. package/dist/customer/__tests__/customer.test.js.map +1 -0
  121. package/dist/customer/index.d.ts +48 -0
  122. package/dist/customer/index.js +132 -0
  123. package/dist/customer/index.js.map +1 -0
  124. package/dist/feedback/__tests__/feedback.test.d.ts +1 -0
  125. package/dist/feedback/__tests__/feedback.test.js +80 -0
  126. package/dist/feedback/__tests__/feedback.test.js.map +1 -0
  127. package/dist/feedback/index.d.ts +48 -0
  128. package/dist/feedback/index.js +97 -0
  129. package/dist/feedback/index.js.map +1 -0
  130. package/dist/flags/__tests__/flags.test.d.ts +1 -0
  131. package/dist/flags/__tests__/flags.test.js +301 -0
  132. package/dist/flags/__tests__/flags.test.js.map +1 -0
  133. package/dist/flags/client.d.ts +131 -0
  134. package/dist/flags/client.js +301 -0
  135. package/dist/flags/client.js.map +1 -0
  136. package/dist/flags/engine.d.ts +200 -0
  137. package/dist/flags/engine.js +677 -0
  138. package/dist/flags/engine.js.map +1 -0
  139. package/dist/flags/index.d.ts +4 -0
  140. package/dist/flags/index.js +16 -0
  141. package/dist/flags/index.js.map +1 -0
  142. package/dist/guard/__tests__/findings.test.js +28 -9
  143. package/dist/guard/__tests__/findings.test.js.map +1 -1
  144. package/dist/guard/__tests__/report.test.js +67 -0
  145. package/dist/guard/__tests__/report.test.js.map +1 -1
  146. package/dist/guard/base.d.ts +2 -0
  147. package/dist/guard/base.js +4 -0
  148. package/dist/guard/base.js.map +1 -1
  149. package/dist/guard/findings.d.ts +12 -2
  150. package/dist/guard/findings.js +19 -49
  151. package/dist/guard/findings.js.map +1 -1
  152. package/dist/guard/heartbeat.d.ts +34 -0
  153. package/dist/guard/heartbeat.js +83 -0
  154. package/dist/guard/heartbeat.js.map +1 -0
  155. package/dist/guard/integration.d.ts +33 -3
  156. package/dist/guard/integration.js +106 -32
  157. package/dist/guard/integration.js.map +1 -1
  158. package/dist/guard/pipeline.d.ts +10 -1
  159. package/dist/guard/pipeline.js +14 -0
  160. package/dist/guard/pipeline.js.map +1 -1
  161. package/dist/guard/report.d.ts +24 -1
  162. package/dist/guard/report.js +67 -18
  163. package/dist/guard/report.js.map +1 -1
  164. package/dist/index.d.ts +90 -0
  165. package/dist/index.js +225 -3
  166. package/dist/index.js.map +1 -1
  167. package/dist/instrumentation/__tests__/langchain-wrapper.test.js +1 -1
  168. package/dist/instrumentation/__tests__/langchain-wrapper.test.js.map +1 -1
  169. package/dist/instrumentation/anthropic/wrapper.js +4 -0
  170. package/dist/instrumentation/anthropic/wrapper.js.map +1 -1
  171. package/dist/instrumentation/mcp/wrapper.js +7 -1
  172. package/dist/instrumentation/mcp/wrapper.js.map +1 -1
  173. package/dist/instrumentation/openai/wrapper.js +6 -0
  174. package/dist/instrumentation/openai/wrapper.js.map +1 -1
  175. package/dist/masking.d.ts +42 -0
  176. package/dist/masking.js +197 -0
  177. package/dist/masking.js.map +1 -0
  178. package/dist/otel/__tests__/app-logs-safety.test.d.ts +1 -0
  179. package/dist/otel/__tests__/app-logs-safety.test.js +129 -0
  180. package/dist/otel/__tests__/app-logs-safety.test.js.map +1 -0
  181. package/dist/otel/__tests__/app-logs.test.d.ts +1 -0
  182. package/dist/otel/__tests__/app-logs.test.js +269 -0
  183. package/dist/otel/__tests__/app-logs.test.js.map +1 -0
  184. package/dist/otel/__tests__/log-auto.test.d.ts +1 -0
  185. package/dist/otel/__tests__/log-auto.test.js +125 -0
  186. package/dist/otel/__tests__/log-auto.test.js.map +1 -0
  187. package/dist/otel/__tests__/log-bridges.test.d.ts +1 -0
  188. package/dist/otel/__tests__/log-bridges.test.js +170 -0
  189. package/dist/otel/__tests__/log-bridges.test.js.map +1 -0
  190. package/dist/otel/__tests__/system-metrics.test.d.ts +1 -0
  191. package/dist/otel/__tests__/system-metrics.test.js +79 -0
  192. package/dist/otel/__tests__/system-metrics.test.js.map +1 -0
  193. package/dist/otel/app-logs.d.ts +80 -0
  194. package/dist/otel/app-logs.js +195 -0
  195. package/dist/otel/app-logs.js.map +1 -0
  196. package/dist/otel/build-info.d.ts +23 -0
  197. package/dist/otel/build-info.js +176 -0
  198. package/dist/otel/build-info.js.map +1 -0
  199. package/dist/otel/code-location.d.ts +49 -0
  200. package/dist/otel/code-location.js +233 -0
  201. package/dist/otel/code-location.js.map +1 -0
  202. package/dist/otel/events.d.ts +1 -0
  203. package/dist/otel/events.js +21 -12
  204. package/dist/otel/events.js.map +1 -1
  205. package/dist/otel/log-auto.d.ts +44 -0
  206. package/dist/otel/log-auto.js +200 -0
  207. package/dist/otel/log-auto.js.map +1 -0
  208. package/dist/otel/log-bridges.d.ts +54 -0
  209. package/dist/otel/log-bridges.js +307 -0
  210. package/dist/otel/log-bridges.js.map +1 -0
  211. package/dist/otel/system-metrics.d.ts +39 -0
  212. package/dist/otel/system-metrics.js +166 -0
  213. package/dist/otel/system-metrics.js.map +1 -0
  214. package/dist/otel/tracing.js +12 -1
  215. package/dist/otel/tracing.js.map +1 -1
  216. package/dist/pricing.json +100 -42
  217. package/dist/protect/__tests__/protect.test.d.ts +1 -0
  218. package/dist/protect/__tests__/protect.test.js +210 -0
  219. package/dist/protect/__tests__/protect.test.js.map +1 -0
  220. package/dist/protect/index.d.ts +103 -0
  221. package/dist/protect/index.js +196 -0
  222. package/dist/protect/index.js.map +1 -0
  223. package/dist/protect/mcp.d.ts +13 -0
  224. package/dist/protect/mcp.js +49 -0
  225. package/dist/protect/mcp.js.map +1 -0
  226. package/dist/protect/mode.d.ts +89 -0
  227. package/dist/protect/mode.js +134 -0
  228. package/dist/protect/mode.js.map +1 -0
  229. package/dist/rasp/__tests__/auto-http.test.d.ts +1 -0
  230. package/dist/rasp/__tests__/auto-http.test.js +136 -0
  231. package/dist/rasp/__tests__/auto-http.test.js.map +1 -0
  232. package/dist/rasp/__tests__/baseline.test.d.ts +1 -0
  233. package/dist/rasp/__tests__/baseline.test.js +86 -0
  234. package/dist/rasp/__tests__/baseline.test.js.map +1 -0
  235. package/dist/rasp/__tests__/benign-corpus.test.d.ts +1 -0
  236. package/dist/rasp/__tests__/benign-corpus.test.js +68 -0
  237. package/dist/rasp/__tests__/benign-corpus.test.js.map +1 -0
  238. package/dist/rasp/__tests__/capture-context.test.d.ts +1 -0
  239. package/dist/rasp/__tests__/capture-context.test.js +135 -0
  240. package/dist/rasp/__tests__/capture-context.test.js.map +1 -0
  241. package/dist/rasp/__tests__/code-hook.test.d.ts +1 -0
  242. package/dist/rasp/__tests__/code-hook.test.js +218 -0
  243. package/dist/rasp/__tests__/code-hook.test.js.map +1 -0
  244. package/dist/rasp/__tests__/config-client.test.d.ts +1 -0
  245. package/dist/rasp/__tests__/config-client.test.js +283 -0
  246. package/dist/rasp/__tests__/config-client.test.js.map +1 -0
  247. package/dist/rasp/__tests__/corpus.d.ts +13 -0
  248. package/dist/rasp/__tests__/corpus.js +111 -0
  249. package/dist/rasp/__tests__/corpus.js.map +1 -0
  250. package/dist/rasp/__tests__/custom-rules.test.d.ts +1 -0
  251. package/dist/rasp/__tests__/custom-rules.test.js +276 -0
  252. package/dist/rasp/__tests__/custom-rules.test.js.map +1 -0
  253. package/dist/rasp/__tests__/db-hook.test.d.ts +1 -0
  254. package/dist/rasp/__tests__/db-hook.test.js +438 -0
  255. package/dist/rasp/__tests__/db-hook.test.js.map +1 -0
  256. package/dist/rasp/__tests__/deception.test.d.ts +1 -0
  257. package/dist/rasp/__tests__/deception.test.js +567 -0
  258. package/dist/rasp/__tests__/deception.test.js.map +1 -0
  259. package/dist/rasp/__tests__/detect.test.d.ts +1 -0
  260. package/dist/rasp/__tests__/detect.test.js +276 -0
  261. package/dist/rasp/__tests__/detect.test.js.map +1 -0
  262. package/dist/rasp/__tests__/esm-exports.test.d.ts +1 -0
  263. package/dist/rasp/__tests__/esm-exports.test.js +20 -0
  264. package/dist/rasp/__tests__/esm-exports.test.js.map +1 -0
  265. package/dist/rasp/__tests__/fs-hook.test.d.ts +1 -0
  266. package/dist/rasp/__tests__/fs-hook.test.js +241 -0
  267. package/dist/rasp/__tests__/fs-hook.test.js.map +1 -0
  268. package/dist/rasp/__tests__/hook-contracts.test.d.ts +1 -0
  269. package/dist/rasp/__tests__/hook-contracts.test.js +201 -0
  270. package/dist/rasp/__tests__/hook-contracts.test.js.map +1 -0
  271. package/dist/rasp/__tests__/hook-kit.d.ts +42 -0
  272. package/dist/rasp/__tests__/hook-kit.js +139 -0
  273. package/dist/rasp/__tests__/hook-kit.js.map +1 -0
  274. package/dist/rasp/__tests__/http-hook.test.d.ts +1 -0
  275. package/dist/rasp/__tests__/http-hook.test.js +250 -0
  276. package/dist/rasp/__tests__/http-hook.test.js.map +1 -0
  277. package/dist/rasp/__tests__/iast.test.d.ts +1 -0
  278. package/dist/rasp/__tests__/iast.test.js +361 -0
  279. package/dist/rasp/__tests__/iast.test.js.map +1 -0
  280. package/dist/rasp/__tests__/include-hook.test.d.ts +1 -0
  281. package/dist/rasp/__tests__/include-hook.test.js +122 -0
  282. package/dist/rasp/__tests__/include-hook.test.js.map +1 -0
  283. package/dist/rasp/__tests__/middleware.test.d.ts +1 -0
  284. package/dist/rasp/__tests__/middleware.test.js +302 -0
  285. package/dist/rasp/__tests__/middleware.test.js.map +1 -0
  286. package/dist/rasp/__tests__/process-hook.test.d.ts +1 -0
  287. package/dist/rasp/__tests__/process-hook.test.js +238 -0
  288. package/dist/rasp/__tests__/process-hook.test.js.map +1 -0
  289. package/dist/rasp/__tests__/reporter.test.d.ts +1 -0
  290. package/dist/rasp/__tests__/reporter.test.js +114 -0
  291. package/dist/rasp/__tests__/reporter.test.js.map +1 -0
  292. package/dist/rasp/__tests__/route.test.d.ts +1 -0
  293. package/dist/rasp/__tests__/route.test.js +46 -0
  294. package/dist/rasp/__tests__/route.test.js.map +1 -0
  295. package/dist/rasp/__tests__/rule-expr.test.d.ts +1 -0
  296. package/dist/rasp/__tests__/rule-expr.test.js +151 -0
  297. package/dist/rasp/__tests__/rule-expr.test.js.map +1 -0
  298. package/dist/rasp/__tests__/rule-phases.test.d.ts +1 -0
  299. package/dist/rasp/__tests__/rule-phases.test.js +271 -0
  300. package/dist/rasp/__tests__/rule-phases.test.js.map +1 -0
  301. package/dist/rasp/__tests__/sinks-hook.test.d.ts +1 -0
  302. package/dist/rasp/__tests__/sinks-hook.test.js +177 -0
  303. package/dist/rasp/__tests__/sinks-hook.test.js.map +1 -0
  304. package/dist/rasp/__tests__/targets.test.d.ts +1 -0
  305. package/dist/rasp/__tests__/targets.test.js +82 -0
  306. package/dist/rasp/__tests__/targets.test.js.map +1 -0
  307. package/dist/rasp/__tests__/template-hook.test.d.ts +1 -0
  308. package/dist/rasp/__tests__/template-hook.test.js +254 -0
  309. package/dist/rasp/__tests__/template-hook.test.js.map +1 -0
  310. package/dist/rasp/__tests__/upload.test.d.ts +1 -0
  311. package/dist/rasp/__tests__/upload.test.js +81 -0
  312. package/dist/rasp/__tests__/upload.test.js.map +1 -0
  313. package/dist/rasp/__tests__/vpatch-engine.test.d.ts +1 -0
  314. package/dist/rasp/__tests__/vpatch-engine.test.js +177 -0
  315. package/dist/rasp/__tests__/vpatch-engine.test.js.map +1 -0
  316. package/dist/rasp/auto-http.d.ts +6 -0
  317. package/dist/rasp/auto-http.js +194 -0
  318. package/dist/rasp/auto-http.js.map +1 -0
  319. package/dist/rasp/baseline.d.ts +35 -0
  320. package/dist/rasp/baseline.js +91 -0
  321. package/dist/rasp/baseline.js.map +1 -0
  322. package/dist/rasp/capture.d.ts +76 -0
  323. package/dist/rasp/capture.js +304 -0
  324. package/dist/rasp/capture.js.map +1 -0
  325. package/dist/rasp/config-client.d.ts +122 -0
  326. package/dist/rasp/config-client.js +337 -0
  327. package/dist/rasp/config-client.js.map +1 -0
  328. package/dist/rasp/context.d.ts +65 -0
  329. package/dist/rasp/context.js +68 -0
  330. package/dist/rasp/context.js.map +1 -0
  331. package/dist/rasp/custom-rules.d.ts +68 -0
  332. package/dist/rasp/custom-rules.js +222 -0
  333. package/dist/rasp/custom-rules.js.map +1 -0
  334. package/dist/rasp/data/default-rules.json +199 -0
  335. package/dist/rasp/data/default-sinks.json +4 -0
  336. package/dist/rasp/data/dnslog-domains.json +11 -0
  337. package/dist/rasp/deception.d.ts +70 -0
  338. package/dist/rasp/deception.js +220 -0
  339. package/dist/rasp/deception.js.map +1 -0
  340. package/dist/rasp/detect.d.ts +68 -0
  341. package/dist/rasp/detect.js +671 -0
  342. package/dist/rasp/detect.js.map +1 -0
  343. package/dist/rasp/engine.d.ts +107 -0
  344. package/dist/rasp/engine.js +330 -0
  345. package/dist/rasp/engine.js.map +1 -0
  346. package/dist/rasp/hooks/code.d.ts +20 -0
  347. package/dist/rasp/hooks/code.js +334 -0
  348. package/dist/rasp/hooks/code.js.map +1 -0
  349. package/dist/rasp/hooks/db.d.ts +48 -0
  350. package/dist/rasp/hooks/db.js +322 -0
  351. package/dist/rasp/hooks/db.js.map +1 -0
  352. package/dist/rasp/hooks/esm.d.ts +7 -0
  353. package/dist/rasp/hooks/esm.js +18 -0
  354. package/dist/rasp/hooks/esm.js.map +1 -0
  355. package/dist/rasp/hooks/fs.d.ts +3 -0
  356. package/dist/rasp/hooks/fs.js +206 -0
  357. package/dist/rasp/hooks/fs.js.map +1 -0
  358. package/dist/rasp/hooks/http.d.ts +4 -0
  359. package/dist/rasp/hooks/http.js +213 -0
  360. package/dist/rasp/hooks/http.js.map +1 -0
  361. package/dist/rasp/hooks/include.d.ts +9 -0
  362. package/dist/rasp/hooks/include.js +79 -0
  363. package/dist/rasp/hooks/include.js.map +1 -0
  364. package/dist/rasp/hooks/marks.d.ts +2 -0
  365. package/dist/rasp/hooks/marks.js +16 -0
  366. package/dist/rasp/hooks/marks.js.map +1 -0
  367. package/dist/rasp/hooks/process.d.ts +22 -0
  368. package/dist/rasp/hooks/process.js +191 -0
  369. package/dist/rasp/hooks/process.js.map +1 -0
  370. package/dist/rasp/hooks/sinks.d.ts +54 -0
  371. package/dist/rasp/hooks/sinks.js +329 -0
  372. package/dist/rasp/hooks/sinks.js.map +1 -0
  373. package/dist/rasp/hooks/template.d.ts +21 -0
  374. package/dist/rasp/hooks/template.js +152 -0
  375. package/dist/rasp/hooks/template.js.map +1 -0
  376. package/dist/rasp/iast-rules.d.ts +82 -0
  377. package/dist/rasp/iast-rules.js +240 -0
  378. package/dist/rasp/iast-rules.js.map +1 -0
  379. package/dist/rasp/iast-scope.d.ts +29 -0
  380. package/dist/rasp/iast-scope.js +54 -0
  381. package/dist/rasp/iast-scope.js.map +1 -0
  382. package/dist/rasp/iast.d.ts +81 -0
  383. package/dist/rasp/iast.js +272 -0
  384. package/dist/rasp/iast.js.map +1 -0
  385. package/dist/rasp/index.d.ts +52 -0
  386. package/dist/rasp/index.js +280 -0
  387. package/dist/rasp/index.js.map +1 -0
  388. package/dist/rasp/lexers.d.ts +32 -0
  389. package/dist/rasp/lexers.js +444 -0
  390. package/dist/rasp/lexers.js.map +1 -0
  391. package/dist/rasp/middleware.d.ts +155 -0
  392. package/dist/rasp/middleware.js +568 -0
  393. package/dist/rasp/middleware.js.map +1 -0
  394. package/dist/rasp/model-output.d.ts +14 -0
  395. package/dist/rasp/model-output.js +107 -0
  396. package/dist/rasp/model-output.js.map +1 -0
  397. package/dist/rasp/normalize.d.ts +23 -0
  398. package/dist/rasp/normalize.js +140 -0
  399. package/dist/rasp/normalize.js.map +1 -0
  400. package/dist/rasp/reporter.d.ts +60 -0
  401. package/dist/rasp/reporter.js +230 -0
  402. package/dist/rasp/reporter.js.map +1 -0
  403. package/dist/rasp/rule-expr.d.ts +236 -0
  404. package/dist/rasp/rule-expr.js +1253 -0
  405. package/dist/rasp/rule-expr.js.map +1 -0
  406. package/dist/rasp/template.d.ts +53 -0
  407. package/dist/rasp/template.js +336 -0
  408. package/dist/rasp/template.js.map +1 -0
  409. package/dist/rasp/types.d.ts +110 -0
  410. package/dist/rasp/types.js +51 -0
  411. package/dist/rasp/types.js.map +1 -0
  412. package/dist/rasp/vpatch.d.ts +55 -0
  413. package/dist/rasp/vpatch.js +120 -0
  414. package/dist/rasp/vpatch.js.map +1 -0
  415. package/dist/register.js +52 -11
  416. package/dist/register.js.map +1 -1
  417. package/dist/semantic-convention.d.ts +2 -0
  418. package/dist/semantic-convention.js +5 -0
  419. package/dist/semantic-convention.js.map +1 -1
  420. package/dist/types.d.ts +96 -0
  421. package/dist/zero-code.d.ts +8 -0
  422. package/dist/zero-code.js +38 -0
  423. package/dist/zero-code.js.map +1 -0
  424. package/package.json +18 -2
package/README.md CHANGED
@@ -26,52 +26,52 @@ This project proudly follows and maintains the [Semantic Conventions](https://gi
26
26
 
27
27
  | LLMs |
28
28
  | ------------------------------------------------------------------------------------------- |
29
- | [✅ OpenAI](https://docs.shield360.ai/latest/integrations/openai) |
30
- | [✅ Anthropic](https://docs.shield360.ai/latest/integrations/anthropic) |
31
- | [✅ Cohere](https://docs.shield360.ai/latest/integrations/cohere) |
32
- | [✅ Groq](https://docs.shield360.ai/latest/integrations/groq) |
33
- | [✅ Mistral](https://docs.shield360.ai/latest/integrations/mistral) |
34
- | [✅ Google AI Studio](https://docs.shield360.ai/latest/integrations/google-ai-studio) |
35
- | [✅ Google Vertex AI](https://docs.shield360.ai/latest/integrations/vertex-ai) *(via `@google-cloud/vertexai`)* |
36
- | [✅ Together AI](https://docs.shield360.ai/latest/integrations/together) |
37
- | [✅ Ollama](https://docs.shield360.ai/latest/integrations/ollama) |
38
- | [✅ AWS Bedrock](https://docs.shield360.ai/latest/integrations/bedrock) |
39
- | [✅ Hugging Face](https://docs.shield360.ai/latest/integrations/huggingface) *(Inference API + local Transformers.js)* |
40
- | [✅ Replicate](https://docs.shield360.ai/latest/integrations/replicate) |
41
- | [✅ Azure OpenAI](https://docs.shield360.ai/latest/integrations/azure-openai) *(via OpenAI SDK)* |
29
+ | [✅ OpenAI](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/openai.mdx) |
30
+ | [✅ Anthropic](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/anthropic.mdx) |
31
+ | [✅ Cohere](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/cohere.mdx) |
32
+ | [✅ Groq](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/groq.mdx) |
33
+ | [✅ Mistral](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/mistral.mdx) |
34
+ | [✅ Google AI Studio](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/google-ai-studio.mdx) |
35
+ | [✅ Google Vertex AI](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/vertexai.mdx) *(via `@google-cloud/vertexai`)* |
36
+ | [✅ Together AI](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/together.mdx) |
37
+ | [✅ Ollama](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/ollama.mdx) |
38
+ | [✅ AWS Bedrock](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/bedrock.mdx) |
39
+ | [✅ Hugging Face](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/huggingface.mdx) *(Inference API + local Transformers.js)* |
40
+ | [✅ Replicate](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/replicate.mdx) |
41
+ | [✅ Azure OpenAI](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/azure-openai.mdx) *(via OpenAI SDK)* |
42
42
 
43
43
  | Audio / Speech |
44
44
  | ------------------------------------------------------------------------------------------- |
45
- | [✅ ElevenLabs](https://docs.shield360.ai/latest/sdk/integrations/elevenlabs) |
45
+ | [✅ ElevenLabs](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/elevenlabs.mdx) |
46
46
 
47
47
  | Vector Databases |
48
48
  | ------------------------------------------------------------------------------------------- |
49
- | [✅ Chroma](https://docs.shield360.ai/latest/integrations/chromadb) |
50
- | [✅ Pinecone](https://docs.shield360.ai/latest/integrations/pinecone) |
51
- | [✅ Qdrant](https://docs.shield360.ai/latest/integrations/qdrant) |
52
- | [✅ Milvus](https://docs.shield360.ai/latest/integrations/milvus) |
49
+ | [✅ Chroma](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/chromadb.mdx) |
50
+ | [✅ Pinecone](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/pinecone.mdx) |
51
+ | [✅ Qdrant](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/qdrant.mdx) |
52
+ | [✅ Milvus](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/milvus.mdx) |
53
53
 
54
54
  | Frameworks |
55
55
  | ------------------------------------------------------------------------------------------- |
56
- | [✅ LangChain](https://docs.shield360.ai/latest/integrations/langchain) |
57
- | [✅ LlamaIndex](https://docs.shield360.ai/latest/integrations/llama-index) |
58
- | [✅ Vercel AI SDK](https://docs.shield360.ai/latest/integrations/vercel-ai) |
56
+ | [✅ LangChain](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/langchain.mdx) |
57
+ | [✅ LlamaIndex](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/llama-index.mdx) |
58
+ | [✅ Vercel AI SDK](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/integrations/vercel-ai.mdx) |
59
59
 
60
60
  ## Supported Destinations
61
61
 
62
- - [✅ OpenTelemetry Collector](https://docs.shield360.ai/latest/connections/otelcol)
63
- - [✅ Prometheus + Tempo](https://docs.shield360.ai/latest/connections/prometheus-tempo)
64
- - [✅ Prometheus + Jaeger](https://docs.shield360.ai/latest/connections/prometheus-jaeger)
65
- - [✅ Grafana Cloud](https://docs.shield360.ai/latest/connections/grafanacloud)
66
- - [✅ New Relic](https://docs.shield360.ai/latest/connections/new-relic)
67
- - [✅ Elastic](https://docs.shield360.ai/latest/connections/elastic)
68
- - [✅ HyperDX](https://docs.shield360.ai/latest/connections/hyperdx)
69
- - [✅ DataDog](https://docs.shield360.ai/latest/connections/datadog)
70
- - [✅ SigNoz](https://docs.shield360.ai/latest/connections/signoz)
71
- - [✅ OneUptime](https://docs.shield360.ai/latest/connections/oneuptime)
72
- - [✅ Dynatrace](https://docs.shield360.ai/latest/connections/dynatrace)
73
- - [✅ OpenObserve](https://docs.shield360.ai/latest/connections/openobserve)
74
- - [✅ Highlight.io](https://docs.shield360.ai/latest/connections/highlight)
62
+ - [✅ OpenTelemetry Collector](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/otelcol.mdx)
63
+ - [✅ Prometheus + Tempo](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/prometheus-tempo.mdx)
64
+ - [✅ Prometheus + Jaeger](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/prometheus-jaeger.mdx)
65
+ - [✅ Grafana Cloud](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/grafanacloud.mdx)
66
+ - [✅ New Relic](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/new-relic.mdx)
67
+ - [✅ Elastic](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/elastic.mdx)
68
+ - [✅ HyperDX](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/hyperdx.mdx)
69
+ - [✅ DataDog](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/datadog.mdx)
70
+ - [✅ SigNoz](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/signoz.mdx)
71
+ - [✅ OneUptime](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/oneuptime.mdx)
72
+ - [✅ Dynatrace](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/dynatrace.mdx)
73
+ - [✅ OpenObserve](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/openobserve.mdx)
74
+ - [✅ Highlight.io](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/highlight.mdx)
75
75
 
76
76
  ## Supported Metrics
77
77
 
@@ -176,11 +176,53 @@ export OTEL_EXPORTER_OTLP_HEADERS = "YOUR_OTEL_ENDPOINT_AUTH"
176
176
  ### Step 3: Visualize and Optimize!
177
177
  Now that your LLM observability data is being collected and sent to configured OpenTelemetry destination, the next step is to visualize and analyze this data. This will help you understand your LLM application's performance and behavior and identify where it can be improved.
178
178
 
179
- If you want to use Shield360's Observability Dashboard to monitor LLM usage—like cost, tokens, and user interactions—please check out our [Quickstart Guide](https://docs.shield360.ai/latest/quickstart).
179
+ If you want to use Shield360's Observability Dashboard to monitor LLM usage—like cost, tokens, and user interactions—please check out our [Quickstart Guide](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/quickstart-ai-observability.mdx).
180
180
 
181
- If you're sending metrics and traces to other observability tools, take a look at our [Connections Guide](https://docs.shield360.ai/latest/destinations/intro) to start using a pre-built dashboard we have created for these tools.
181
+ If you're sending metrics and traces to other observability tools, take a look at our [Connections Guide](https://github.com/ThinkfleetAI/shield360-js/blob/main/docs/destinations/overview.mdx) to start using a pre-built dashboard we have created for these tools.
182
182
 
183
183
 
184
+ ## Zero-code setup
185
+
186
+ Turn on Shield360, telemetry and runtime protection (RASP), without changing or rebuilding the app. Install the package next to the app and preload `shield360/register`; everything is configured with environment variables.
187
+
188
+ ```bash
189
+ npm install shield360
190
+
191
+ export OTEL_SERVICE_NAME=orders-api
192
+ export SHIELD360_URL=https://app.shield360.ai
193
+ export SHIELD360_API_KEY=... # from Settings > API keys
194
+ export SHIELD360_RASP=monitor # or block; leave unset for telemetry only
195
+ export SHIELD360_CONFIG_PUBLIC_KEY='{"kty":"OKP",...}' # Runtime protection settings
196
+
197
+ node --require shield360/register app.js # CommonJS
198
+ node --import shield360/register app.mjs # ES modules
199
+ NODE_OPTIONS="--require shield360/register" npm start
200
+ ```
201
+
202
+ What the preload does, before the app's first line runs:
203
+
204
+ - Calls `Shield360.init()` with the settings from the environment (`OTEL_SERVICE_NAME`, `OTEL_DEPLOYMENT_ENVIRONMENT`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_HEADERS`, `OTEL_RESOURCE_ATTRIBUTES`, `SHIELD360_URL`, `SHIELD360_API_KEY`, `SHIELD360_DISABLED_INSTRUMENTORS` and the other `SHIELD360_*` variables `init()` reads).
205
+ - With `SHIELD360_RASP=monitor|block`, installs the RASP sink hooks before the app takes its own references to `child_process`, `fs` and the database drivers, and captures every request served by an `http` or `https` server (Express, Fastify, Koa, Nest and raw `http`), so no middleware is needed. Adding `raspExpress()` or another framework middleware as well is fine: it takes over the request with its richer view and nothing is reported twice.
206
+ - Registers the OpenTelemetry ESM loader hook (`module.register`, Node.js 18.19 / 20.6 and later), so instrumentations also patch packages the app loads with `import`. Set `SHIELD360_ESM_HOOK=off` to leave it out.
207
+
208
+ Rules it keeps:
209
+
210
+ - **Fail open.** If Shield360 cannot start, it prints one warning and the app starts without it.
211
+ - **No double start.** If the app also calls `Shield360.init()` itself, that call does nothing (one log line says so); the environment settings stay in force.
212
+
213
+ Limits:
214
+
215
+ - In block mode without framework middleware, a virtual patch that matches the request answers 403 before the app sees it, and so does a block in a raw `http` handler, synchronous or `async` (a block after an `await` is answered, not left as an unhandled rejection). Frameworks catch errors from their route handlers themselves, so a block raised at a sink inside an Express, Koa or Fastify handler gets the framework's error response (a 500 in Express) unless that framework's RASP middleware or error handler is added.
216
+ - `NODE_OPTIONS` is inherited by child Node.js processes, which start Shield360 too. Use `node --require` on the app's command when that is not wanted.
217
+ - Bundled apps (esbuild, webpack, `ncc`) and single-executable apps inline their dependencies, so instrumentations that patch packages by name do not see them. The RASP hooks on Node's built-in modules still apply. Keep instrumented packages external, such as the database driver (`external: ['pg']` in esbuild, installed beside the bundle), or call `Shield360.init()` in code.
218
+ - `NODE_OPTIONS="--require shield360/register"` also works for ES module apps. Node runs the preload again in its module-hooks thread, and the preload does nothing there.
219
+ - Packages imported as ES modules are patched only through the loader hook; with `SHIELD360_ESM_HOOK=off`, only CommonJS loads are instrumented.
220
+
221
+ | Variable | Default | Meaning |
222
+ |---|---|---|
223
+ | `SHIELD360_RASP` | unset (off) | `monitor` or `block` turns RASP on; `off` always wins. |
224
+ | `SHIELD360_ESM_HOOK` | on | `off` skips the ES module loader hook. New for the zero-code entry. |
225
+
184
226
  ## Configuration
185
227
 
186
228
  ### Observability - `Shield360.init()`
@@ -195,11 +237,67 @@ Below is a detailed overview of the configuration options available, allowing yo
195
237
  | `otlpEndpoint` | Specifies the OTLP endpoint for transmitting telemetry data. | `undefined` | No |
196
238
  | `otlpHeaders` | Defines headers for the OTLP exporter, useful for backends requiring authentication. | `undefined` | No |
197
239
  | `disableBatch` | A flag to disable batch span processing, favoring immediate dispatch. | `true` | No |
198
- | `traceContent` | Enables tracing of content for deeper insights. | `true` | No |
240
+ | `captureMessageContent` | Records prompt and completion text. Off unless set here or with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`. | `false` | No |
199
241
  | `disabledInstrumentations` | List of instrumentations to disable. | `undefined` | No |
200
242
  | `instrumentations` | Object of instrumentation modules for manual patching | `undefined` | No |
201
243
  | `pricingJson` | URL or file path of the pricing JSON file. | Bundled `pricing.json` | No |
202
244
  | `collectGpuStats` | Flag to enable or disable GPU metrics collection (NVIDIA via `nvidia-smi`, AMD via `rocm-smi`). | `false` | No |
245
+ | `captureCodeLocation` | Adds the calling function, file and line to GenAI spans. Also `SHIELD360_CAPTURE_CODE_LOCATION=false`. | `true` | No |
246
+ | `codeRoot` | Directory stripped from reported file paths. Also `SHIELD360_CODE_ROOT`. | git root, then working directory | No |
247
+ | `serviceVersion` | Release recorded as the `service.version` resource attribute. Also `SHIELD360_SERVICE_VERSION` or `SHIELD360_RELEASE`. | `undefined` | No |
248
+
249
+ > **Message content is off by default.** Prompt and completion text is recorded only when you pass `captureMessageContent: true` (or set `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`). Even then, Shield360 stores it only if your organization's [Data retention](https://shield360.ai) setting allows it. Before this release the default was on.
250
+
251
+ ### Code location and build attributes
252
+
253
+ Every GenAI span (one with `gen_ai.operation.name`) records where your code made the call:
254
+
255
+ | Attribute | Value |
256
+ | --- | --- |
257
+ | `code.function.name` | Calling function, such as `BillingService.summarise`. Left out for anonymous functions. |
258
+ | `code.file.path` | File path relative to `codeRoot`, the git root or the working directory. No home directory (yours or another user's) or CI workspace prefix is sent. |
259
+ | `code.line.number` | Line of the call. |
260
+
261
+ Frames inside Shield360, OpenTelemetry, `node_modules` and Node internals are skipped. Only names, paths and line numbers are recorded, never argument values or source text. Turn it off with `captureCodeLocation: false` or `SHIELD360_CAPTURE_CODE_LOCATION=false` (`0`, `no` and `off` also work); the init option wins over the environment variable.
262
+
263
+ At startup Shield360 also adds these resource attributes, so costs and traces can be tied to a release:
264
+
265
+ | Attribute | Sources, first match wins |
266
+ | --- | --- |
267
+ | `service.version` | `serviceVersion`, `SHIELD360_SERVICE_VERSION`, `SHIELD360_RELEASE`, `OTEL_SERVICE_VERSION`, `OTEL_RESOURCE_ATTRIBUTES` |
268
+ | `vcs.ref.head.revision` | `SHIELD360_GIT_COMMIT`, `OTEL_RESOURCE_ATTRIBUTES`, CI variables |
269
+ | `vcs.repository.url.full` | `SHIELD360_GIT_REPOSITORY_URL`, `OTEL_RESOURCE_ATTRIBUTES`, CI variables |
270
+ | `vcs.ref.head.name` | `SHIELD360_GIT_BRANCH`, `OTEL_RESOURCE_ATTRIBUTES`, CI variables |
271
+
272
+ CI variables are read from GitHub Actions, GitLab CI, Vercel, Netlify, Render, Railway, Heroku, AWS CodeBuild, Buildkite, CircleCI, Bitbucket Pipelines, Azure Pipelines and Jenkins. An explicit `OTEL_RESOURCE_ATTRIBUTES` value beats CI auto-detection. Values from different providers are never mixed. Commit SHAs that are not 7 to 64 hex characters are dropped, and repository URLs are reduced to `https://host/owner/repo` with any credentials removed.
273
+
274
+ Shield360 groups an application's calls by release (Telemetry → Releases) and compares each release with the one before it. With no release set, the commit the SDK detected is used.
275
+
276
+ ### User feedback - `Shield360.feedback()`
277
+
278
+ Send what one of your users thought of an AI response, against the trace that produced it. Call it from your server with the environment's API key, never from a browser. It never throws; it resolves to `false` when the rating was not accepted.
279
+
280
+ ```typescript
281
+ const traceId = Shield360.getCurrentTraceId(); // keep this with the response
282
+
283
+ // later, when the user rates it
284
+ await Shield360.feedback({ traceId, score: 1 }); // thumbs up (-1 is thumbs down)
285
+ await Shield360.feedback({ traceId, score: 4, comment: 'Close, but missed the date' }); // stars 1-5
286
+ await Shield360.feedback({ traceId, score: 1, scale: 'stars' }); // one star
287
+ ```
288
+
289
+ | Option | Description |
290
+ | --- | --- |
291
+ | `score` | `1` or `-1` (thumbs), or `1` to `5` (stars). |
292
+ | `traceId` | The trace rated. Defaults to the active span's trace. |
293
+ | `spanId` | The span rated, when it was one call in the trace. Optional |
294
+ | `comment` | What the user wrote. Kept only when your organization's Data retention setting stores end users' free text, with secrets and personal data masked. Optional |
295
+ | `scale` | `'thumbs'` or `'stars'`. Optional |
296
+ | `userId`, `tenantId` | Your end user and their tenant. Kept only as a keyed hash. Optional |
297
+ | `metadata` | A flat object of up to 20 short values. Optional |
298
+ | `feedbackId` | Your id for the rating; sending it again replaces the earlier one. Optional |
299
+
300
+ Ratings show on the trace and under Telemetry → Feedback, by release and by feature flag variant.
203
301
 
204
302
  ### Shield360 Prompt Hub - `Shield360.getPrompt()`
205
303
 
@@ -292,6 +390,263 @@ Shield360.logScore({ name: 'quality', value: 0.85, metadata: { reviewer: 'human'
292
390
  Shield360.logScore({ name: 'category', value: 'accurate' });
293
391
  ```
294
392
 
393
+ ### Agent passports - `shield360/agents`
394
+
395
+ Check the AI agents that call your API or MCP server. A calling agent sends a Shield360 **agent passport** in the `Shield360-Agent` header: a short-lived signed token that says who the agent is, which organization runs it and, when it acts for a person, that it does. The verifier checks it offline against the issuer's published keys, applies your policy and can report each result to your Shield360 project's inbound decision log. A passport never grants access by itself: your own API keys, OAuth tokens and permissions still decide what the call may do.
396
+
397
+ ```typescript
398
+ import { agentVerifier, AGENT_HEADER } from 'shield360/agents';
399
+
400
+ const verifier = agentVerifier({
401
+ audience: 'api.example.com', // your API host or https origin (or several)
402
+ // issuers: ['https://app.shield360.ai'], // default
403
+ policy: { mode: 'observe' }, // local policy, merged over the defaults
404
+ policyFrom: { apiKey: process.env.SHIELD360_INBOUND_API_KEY! }, // follow the console policy (cached 60 s)
405
+ report: { apiKey: process.env.SHIELD360_INBOUND_API_KEY! }, // inbound decision log (batched, never blocks)
406
+ });
407
+
408
+ const result = await verifier.check(request, { route: '/v1/jobs', method: 'POST' });
409
+ // result.status: 'verified' | 'absent' | 'invalid'
410
+ // result.decision: 'allow' | 'deny'; result.blocked: deny in enforce mode
411
+ // result.agent: id, name and, when the issuer sends them, status, environment, risk, owner, reviewedAt, enrolledAt
412
+ // result.operator, result.scopes
413
+ // result.onBehalfOf: { id (a pairwise id), domain, assurance: 'delegated' | 'named' | 'unverified', mandate? }
414
+ ```
415
+
416
+ `check` accepts a Fetch `Request`, a Node `IncomingMessage`, a `Headers` object or header record, or the passport string, and never throws. The API key is sent as `Authorization: Bearer <key>`; `baseUrl` defaults to `SHIELD360_URL` or `https://app.shield360.ai`.
417
+
418
+ | Policy field | Default | Meaning |
419
+ |--------------|---------|---------|
420
+ | `mode` | `observe` | `observe` never blocks and reports what `enforce` would do. |
421
+ | `requireAgent` | `false` | A call without a passport is denied (reason `absent`). |
422
+ | `operators` | `[]` | Allowed operator ids or verified domains; empty allows any verified operator. |
423
+ | `requireOnBehalfOf` | `false` | The passport must say the agent acts for a person. |
424
+ | `minAssurance` | `none` | The weakest on-behalf-of assurance accepted: `unverified`, `named` or `delegated` (strongest). A passport without a person, or with weaker assurance, is denied (reason `assurance_too_low`). A person without a stated assurance counts as `named`. |
425
+ | `online` | `false` | Also ask the issuer, on every call, whether the passport is still good (kill switch, revoked token). Pass `{ online: true }` to `check` for single sensitive operations. |
426
+
427
+ Reason codes, in the order they are checked: `malformed`, `unsupported_alg`, `untrusted_issuer`, `unknown_key`, `bad_signature`, `expired`, `not_yet_valid`, `lifetime_too_long`, `wrong_audience`, `replayed`, `revoked`, `unknown_passport`, then the policy's `absent`, `operator_not_allowed`, `on_behalf_of_required`, `assurance_too_low`. An online check can keep a passport valid but lower its assurance (for example `reason: 'mandate_revoked'`); the result then carries the lower assurance and no mandate. An unreachable issuer on an online check is `issuer_unreachable` and does not deny unless `onlineFailure: 'deny'`.
428
+
429
+ Each passport is accepted once. The default replay store is in memory, per process; with several instances pass a shared one:
430
+
431
+ ```typescript
432
+ const replay = { seen: async (jti: string, expiresAtMs: number) => (await redis.set(`passport:${jti}`, '1', { NX: true, PXAT: expiresAtMs })) === null };
433
+ agentVerifier({ audience: 'api.example.com', replay });
434
+ ```
435
+
436
+ #### Express
437
+
438
+ ```typescript
439
+ app.use(verifier.middleware()); // sets req.shield360Agent; answers 403 { error: 'agent_not_allowed', reason } only when blocked
440
+ app.get('/v1/jobs', (req, res) => {
441
+ const agent = (req as { shield360Agent?: AgentCheck }).shield360Agent; // import type { AgentCheck } from 'shield360/agents'
442
+ res.json({ agent: agent?.agent });
443
+ });
444
+ ```
445
+
446
+ #### Next.js route handlers and other Fetch servers
447
+
448
+ ```typescript
449
+ export async function POST(request: Request) {
450
+ const agent = await verifier.check(request, { route: '/api/jobs', method: 'POST' });
451
+ if (agent.blocked) return Response.json({ error: 'agent_not_allowed', reason: agent.reason }, { status: 403 });
452
+ // ... your own authentication and authorization still apply
453
+ }
454
+ ```
455
+
456
+ Call `await verifier.close()` on shutdown to send queued reports.
457
+
458
+ ## Application logs
459
+
460
+ Off by default. Set `SHIELD360_CAPTURE_LOGS=true` (or the init option) to send the application's own log records to Shield360, on the same endpoint and key as traces. `SHIELD360_LOG_LEVEL` sets the lowest level sent (`info` by default; `trace`, `debug`, `info`, `warn`, `error`, `fatal`). Records carry the active trace and span ids, `service.name` and `deployment.environment`; credentials and opaque tokens in messages and attributes are masked before they leave the process; at most 500 records a second per bridge are sent, in batches, and a failure to send never reaches the application.
461
+
462
+ ```typescript
463
+ import Shield360 from 'shield360';
464
+
465
+ Shield360.init({ captureLogs: true, logLevel: 'info' }); // or SHIELD360_CAPTURE_LOGS=true
466
+
467
+ console.warn('cache miss', { key: 'k' }); // console output is copied (still printed)
468
+ const log = Shield360.logger('checkout'); // or a logger of your own
469
+ log.info('order placed', { 'order.id': 'o-1' });
470
+ ```
471
+
472
+ With application logs on, `init()` bridges these with no code changes:
473
+
474
+ - **console**: `console.debug`, `log`, `info`, `warn` and `error` are copied as well as printed. `Shield360.releaseConsole()` stops this.
475
+ - **winston** (3.x): every `winston.createLogger()` also gets the Shield360 transport, and the logger's own transports are kept.
476
+ - **pino** (7 to 10): every `pino()` logger writes to its own destination and to Shield360 through `pino.multistream`.
477
+
478
+ **Loggers that write lines themselves.** Some apps have their own logger that writes JSON lines straight to `process.stdout` and `process.stderr`, which none of the bridges above see. For them, set `SHIELD360_CAPTURE_STDIO=true` as well (or `captureStdio: true`). Every line written to stdout and stderr is then copied, and the output is unchanged:
479
+
480
+ - a JSON line keeps its level (a name or a pino number), `msg` or `message` as the body, `err` or `error` as `exception.*`, and its other fields as attributes;
481
+ - a plain line is `info` on stdout and `error` on stderr;
482
+ - a line split across writes is joined first.
483
+
484
+ The scope is `stdout` or `stderr`. It replaces the console, winston and pino bridges, which write to the same streams, so each line is sent once. `Shield360.releaseStdio()` stops it.
485
+
486
+ The winston and pino bridges hook `require` the way OpenTelemetry's instrumentations do, so create those loggers after `init()`. For ES modules, or for loggers created earlier, add the bridge yourself:
487
+
488
+ ```typescript
489
+ import pino from 'pino';
490
+ import winston from 'winston';
491
+ import Transport from 'winston-transport';
492
+
493
+ const p = pino({}, pino.multistream([{ stream: process.stdout }, { stream: Shield360.pinoStream() }]));
494
+ const Shield360Transport = Shield360.winstonTransport(Transport);
495
+ const w = winston.createLogger({ transports: [new winston.transports.Console(), new Shield360Transport()] });
496
+ ```
497
+
498
+ The record's scope is the logger name (`console`, `winston`, `pino`, or the name you give). Other attributes:
499
+
500
+ - `code.function.name`, `code.file.path` and `code.line.number` for the call site, unless `captureCodeLocation` is off;
501
+ - `exception.*` for an `Error`;
502
+ - your fields.
503
+
504
+ The SDK's own scopes (`shield360`, `opentelemetry`) and the telemetry pipeline's own messages are never sent. Masking follows the contract every Shield360 SDK shares (`sdk/rasp/README.md`, "Masking"). Credential pairs (`password=…`, `"api_key": "…"`, `Authorization: Bearer …`), bearer credentials and opaque tokens (keys, hashes, base64, JWTs, UUIDs) are masked. So is the whole value of a field named like a credential (`password`, `x-api-key`, `sessionId`), with lists and maps masked item by item. Readable identifiers survive: class names, package and file paths, and stack frames.
505
+
506
+ ## Budget check
507
+
508
+ Ask Shield360 whether this app may spend on a model call. App budgets (Costs → Budgets → App, by service name) and whole-project budgets with enforcement rules apply. Uses `shield360Url`, `shield360ApiKey` and `applicationName` from `init()`.
509
+
510
+ ```typescript
511
+ const check = await Shield360.budgets.check({ model: 'gpt-4o', estimatedCostUsd: 0.02 });
512
+ if (!check.allowed) throw new Error(check.reason);
513
+ ```
514
+
515
+ `check()` never throws, reuses a decision for its ttl while the calls checked with it fit in its headroom, and fails open unless `failClosed` is set (or `SHIELD360_BUDGET_FAIL_CLOSED=1`). Pass `budgetCheck: true` to `init()` (or set `SHIELD360_BUDGET_CHECK=1`) to check before every call to a supported provider; a blocked call throws `BudgetExceededError`.
516
+
517
+ ## Guard reports and heartbeat
518
+
519
+ When `init` is given `guards` and a Shield360 URL and API key (`shield360Url` / `shield360ApiKey`, or `SHIELD360_URL` / `SHIELD360_API_KEY`), the SDK reports to Shield360's `/api/guard/events`:
520
+
521
+ - **Guard decisions:** every decision other than allow (which guard, what it did, the kind of data found). Never the text that was checked.
522
+ - **A heartbeat:** once at start and then about every 5 minutes (jittered), the app's service name, each guard kind's mode (`off`, `warn` or `block`), whether prompt-injection checks are on, and the SDK's name and version. No text, host or user. Shield360 uses it to show that the app's guards are on even when they had nothing to stop, for example for the [Secured by Shield360 badge](https://shield360.ai).
523
+
524
+ Both go through the same background queue, never throw and never delay a call. Turn the heartbeat off with `guardHeartbeat: false` in `init`, or `SHIELD360_GUARD_HEARTBEAT=false`.
525
+
526
+ ## Runtime protection (RASP)
527
+
528
+ Off by default. When on, the SDK keeps the untrusted input of the request it is serving (query, route params, body, cookies, and the `user-agent`, `referer` and `x-forwarded-for` headers), plus the model output its LLM instrumentation sees, in memory for that request only. The contract every Shield360 SDK follows is [`sdk/rasp/README.md`](../rasp/README.md).
529
+
530
+ ### Turn it on
531
+
532
+ ```typescript
533
+ import Shield360, { raspExpress, raspExpressErrorHandler } from 'shield360';
534
+
535
+ Shield360.init({
536
+ applicationName: 'orders-api',
537
+ shield360Url: process.env.SHIELD360_URL,
538
+ shield360ApiKey: process.env.SHIELD360_API_KEY,
539
+ rasp: { enabled: true, configPublicKey: process.env.SHIELD360_CONFIG_PUBLIC_KEY },
540
+ });
541
+
542
+ app.use(express.json());
543
+ app.use(raspExpress()); // after the body parsers
544
+ // ...routes...
545
+ app.use(raspExpressErrorHandler());
546
+ ```
547
+
548
+ Or set `SHIELD360_RASP=monitor` (or `block`) without changing code. `SHIELD360_RASP=off` always wins. To add it without any code at all, middleware included, see [Zero-code setup](#zero-code-setup).
549
+
550
+ | Framework | Use |
551
+ |---|---|
552
+ | Express 4/5 | `app.use(raspExpress())`, and `raspExpressErrorHandler()` last |
553
+ | Fastify | `fastify.register(raspFastify)` (applies to every route) |
554
+ | Koa | `app.use(raspKoa())` |
555
+ | Node `http` | `http.createServer(raspHttp(handler))`; after reading the body yourself, call `recordRequestBody(body, contentType)` |
556
+ | Next.js route handlers (Node.js runtime) | `export const POST = withRasp(async (request) => { ... })` |
557
+
558
+ Custom request rules run in a head phase and a body phase (see "When rules run" in [`sdk/rasp/README.md`](../rasp/README.md)). A request without a body (no `Content-Length` or `Transfer-Encoding`, or `Content-Length: 0`; for Fastify also any `GET` or `HEAD`, whose body Fastify never parses; for Next.js, a request with no body stream) has a known, empty body, so its body phase runs at once. The body stays unknown, and its body phase waits, until a parser sets `req.body` (Express, Koa, raw `http`), the handler calls `recordRequestBody` (raw `http`), or Fastify's `preHandler` sees the parsed body. It never becomes known when no parser reads it, for Fastify multipart without `attachFieldsToBody`, or for a Next.js body already read before `withRasp`.
559
+
560
+ Options (`rasp: { ... }`): `mode` (`monitor` by default, or `block`, `off`), `attacks` (a mode per attack type), `allow` (`commands`, `outboundHosts`, `fileRoots`), `modelOutput` (default `true`), `remote` (default `true`), `configPublicKey`, `allowUnpinnedConfig`, `cacheDir`, `components` (default `true`). `Shield360.disableRasp()` is the runtime kill switch.
561
+
562
+ ### Pin the config key
563
+
564
+ With a Shield360 URL and API key, the SDK polls the app's signed settings (`GET /api/rasp/config`). It trusts them only when they verify against the key you pin. Copy the environment's public key from the app's **Runtime protection** settings in Shield360 into `configPublicKey` or `SHIELD360_CONFIG_PUBLIC_KEY`, as a JWK, a JWKS (`{"keys":[...]}`, so a key rotation can list the old and new key), or the bare base64url `x`.
565
+
566
+ - The keys in the config response are never trusted on their own.
567
+ - With no pinned key, the SDK does not fetch the signed config. It runs its built-in defaults in monitor mode (a local `block` is lowered to `monitor`) and logs one warning.
568
+ - Trust on first use is for development only: `allowUnpinnedConfig: true` or `SHIELD360_CONFIG_TOFU=dev` pins the first key the server sends. Both are ignored when `NODE_ENV` or `ENVIRONMENT` is `production`.
569
+
570
+ The last verified config is cached (OS temp dir, `shield360-rasp/`) and verified again when loaded.
571
+
572
+ ### What this release covers
573
+
574
+ - Request capture for the frameworks above, and model output (OpenAI chat and Responses, Anthropic messages) recorded as untrusted input.
575
+ - The detection engine (SQL token structure, path containment, address class, deserialization and eval taint, XML external entities, reflected XSS that is never blocked). These are pure functions that later sink hooks call.
576
+ - Virtual patches: the built-in rules in [`sdk/rasp/rules/default-rules.json`](../rasp/rules/default-rules.json) are checked against each request before the handler. A blocked request gets a `403` with `{"error","attack","rule","event"}`.
577
+ - `child_process`: `exec`, `execSync`, `spawn`, `spawnSync`, `execFile`, `execFileSync` and `fork` are checked before the process starts, for command injection (shell command lines and `sh -c` scripts), an executable taken from the request, a shell or downloader started during a request (`shell_spawn`, unless listed in `allow.commands`), and a download piped into a shell. `util.promisify(exec)` is checked too. Initialise Shield360 before loading the app's modules: a function copied out of `child_process` before then (`const { exec } = require('child_process')`) is not checked.
578
+ - `fs` and `fs.promises`: reads, opens, writes, appends, deletes, renames (both paths), copies (source and target), directory listings and read/write streams are checked before the call for path traversal (`../` from the request, an absolute path from the request, NUL bytes, paths outside `allow.fileRoots`, well-known secret files) and script writes (`file_write`). A blocked promise call rejects; a blocked callback or sync call throws. File descriptors are not checked.
579
+ - Events (`shield360.rasp.event`, folded and capped at 100 a minute) and a report of the app's package names and versions, sent to Shield360 with the guard reports. Events name the location of an input (`query.id`), never its value.
580
+
581
+ Outbound HTTP: `http`/`https` `request` and `get`, and the global `fetch`, are checked before anything is sent for SSRF from request or model input (internal, metadata, obfuscated and out-of-band hosts, unsafe schemes) and, when `allow.outboundHosts` is set, for hosts outside it (the Shield360 URL and OTLP endpoint are always allowed). `http`/`https` check again at the connection's own DNS lookup, so a name that resolves to an internal address is refused (DNS rebinding). `fetch` resolves the name once before the call, and its own connection may resolve again.
582
+
583
+ Databases: SQL text sent through `pg`, `mysql2`, `mysql`, `sqlite3`, `better-sqlite3`, `mssql`, `tedious`, `oracledb` and `ibm_db` is checked for SQL injection before the driver sends it (structural, per dialect; bound parameters are never part of the text, so parameterised queries pass), and MongoDB filter, update, replacement and pipeline documents (`mongodb` `Collection` methods) for operators that came from the request (`nosql_injection`). Drivers are hooked when the app loads them, including ones loaded before `init`; the SDK's own database tracing keeps working alongside.
584
+
585
+ | Database | Driver and methods | Dialect |
586
+ |---|---|---|
587
+ | SQL Server | `mssql` `Request.query`, `batch`, `execute` (on the base Request the drivers share), `PreparedStatement.prepare`; `tedious` `Connection.execSql`, `execSqlBatch`, `prepare` (the Request's SQL text). A query `mssql` checked is not checked again in the `tedious` call it makes. | `mssql` |
588
+ | Oracle | `oracledb` `Connection.execute`, `executeMany`, `queryStream` (not checked again in the `execute` it runs) | `oracle` |
589
+ | Db2 | `ibm_db` `Database.query`, `querySync`, `queryResult`, `queryResultSync` (and `queryStream` through it), `prepare`, `prepareSync` (string or `{ sql }`) | `db2` |
590
+
591
+ Code, deserialization and XML: `vm` (`runInThisContext`, `runInNewContext`, `runInContext`, `compileFunction`, `new vm.Script`) and the `Function` constructor are checked for code built from request or model input (`code_injection`); `node-serialize` `unserialize`, `funcster` `deepDeserialize` and `js-yaml` `load`/`loadAll` with a schema that can build functions for tainted data (`deserialization`); and `libxmljs`/`libxmljs2` `parseXml` for external entities (`xxe`, by whether the parser resolves them). `eval` is not hooked: replacing it would break direct `eval` in the app (code relying on the caller's scope, such as webpack's eval source maps).
592
+
593
+ Responses: HTML responses (`Content-Type` containing `html`, up to 1 MiB) are checked for request input reflected verbatim (`xss`, monitor only: it never blocks or changes the response).
594
+
595
+ Sink rules: the signed config can name further library functions to check with any of the checks above (for example a driver's raw query method as a SQL sink), as data, without an SDK release. They report only unless a rule says `block` and the attack type is in block mode. See "Sink rules" in [`sdk/rasp/README.md`](../rasp/README.md).
596
+
597
+ Uploads: file names of multipart uploads (raw bodies, or multer's `req.file` / `req.files` with `raspExpress`) are request input, and an upload named like a script (`.php`, `.jsp`, `.sh`, `.exe`…) is reported (`file_write.upload`; blocks when `file_write` blocks). A later write named after the upload is caught by `file_write.script`.
598
+
599
+ Module loading: `require` (and functions from `module.createRequire`) is checked during a request for a module name taken whole from input, or a path from input inside the specifier (`code_injection`, rule `code.include`). Dynamic `import()` is not covered.
600
+
601
+ Security baseline: once per process, shortly after start, RASP reports risky set-up as `baseline` events (report only): TLS checks off, a debugger in production, development mode under a production environment, running as root, an unpinned config key in production, and a Node.js line past its end of life. See "Security baseline" in [`sdk/rasp/README.md`](../rasp/README.md).
602
+
603
+ Deception (decoy paths): off unless the app's **Runtime protection** settings in Shield360 list decoys, paths no real user requests such as `/wp-admin` (the signed config's `deception` block; there is no local way to define them). The middleware checks every request against them first, before anything else. A request for a decoy never reaches your handlers: it gets the 404 your app gives an unknown path (Express's default `Cannot GET /path` page, or `raspExpress({ notFound })` for your own page; Fastify's `reply.callNotFound()`; Koa's `Not Found`; an empty 404 for Next.js route handlers; a plain-text `404 Not Found` for raw `http`), and its client address (the one custom rules see as `request.ip`) is marked suspicious for the configured time. Later requests from that address are reported as `deception.suspicious`; with the decoys' action `block` they get the `403` block response, with `monitor` they go on to the app. Without a pinned key, `block` is lowered to `monitor`, and `attacks.deception: off` turns deception off. The zero-code capture answers decoys itself (plain-text `404 Not Found`, socket peer address) only for a plain `http` listener. For an Express, Koa or Fastify app (Nest included) it leaves deception to the framework's adapter, which knows the trusted client address and the app's own 404, so add `raspExpress()`, `raspKoa()` or `raspFastify` for decoys there. Marks are held in memory per process (at most 10,000 addresses; when full, expired marks go first, then the mark closest to expiring): replicas and cluster workers do not share them. Next.js route handlers have no client address, so a decoy hit there marks nothing. See "Deception" in [`sdk/rasp/README.md`](../rasp/README.md).
604
+
605
+ RASP fails open everywhere: the only error it raises on purpose is `RaspBlockedError`.
606
+
607
+ ## Cost per end customer
608
+
609
+ Building a SaaS product on AI? Tag each call with your own customer (and, optionally, the end user inside it) and Shield360 shows cost and margin per end customer. Every span started while a customer is in effect gets `shield360.customer.tenant_id` and `shield360.customer.user_id`, whichever instrumentation started it.
610
+
611
+ ```typescript
612
+ import Shield360 from 'shield360';
613
+
614
+ // A process-wide default, e.g. in a single-tenant worker. null clears it.
615
+ Shield360.setCustomer({ tenantId: 'acme' });
616
+
617
+ // Per request: wins over the default, follows async work, and nests.
618
+ await Shield360.withCustomer({ tenantId: req.tenantId, userId: req.userId }, async () => {
619
+ await openai.chat.completions.create({ model: 'gpt-4o', messages });
620
+ });
621
+
622
+ Shield360.getCustomer(); // the customer in effect here, or null
623
+ ```
624
+
625
+ Ids may be strings or numbers; they are trimmed and must be 1–256 characters with no control characters. Invalid ids are dropped silently (never thrown, never logged), and a `userId` without a valid `tenantId` is dropped too. A span that already carries `shield360.customer.tenant_id` is left as it is. If you pass your own `tracer` to `init()`, add `CustomerSpanProcessor` to your provider yourself.
626
+
627
+ The SDK sends ids as given and never hashes them: user ids are stored only as a keyed hash on Shield360's side; tenant ids are kept as given unless the organisation opts to hash them.
628
+
629
+ ## AI app security
630
+
631
+ `init({ aiSecurity: true })` turns on checks on the app's own AI traffic, mapped to the OWASP Top 10 for LLM Applications (2025). Every check reports by default and changes nothing; enforcing is set per check (in code, or per app in Shield360 under the app's **Security** tab), and every check fails open. Events carry kinds, masked tokens and keyed fingerprints, never the checked text.
632
+
633
+ ```typescript
634
+ Shield360.init({ applicationName: 'support-bot', aiSecurity: { policy: { output: { mode: 'enforce' } } } });
635
+
636
+ const refund = Shield360.aiSecurity.defineTool({
637
+ name: 'refund_order',
638
+ permissions: { roles: ['support_lead'] }, // checked for the end user before it runs
639
+ execute: async ({ orderId }) => refundOrder(orderId),
640
+ });
641
+
642
+ await Shield360.aiSecurity.withEndUser({ tenantId: 'acme', userId: 'u-42', roles: ['agent'] }, async () => {
643
+ const page = Shield360.aiSecurity.scanContext(fetchedHtml, { source: 'document' }); // indirect injection
644
+ const answer = Shield360.aiSecurity.scanOutput(modelText, { systemPrompt }); // PII, secrets, leaks
645
+ });
646
+ ```
647
+
648
+ With `aiSecurity` on, OpenAI, Anthropic, LangChain retrievers and tools, LlamaIndex retrievers and the MCP client are checked automatically (tool results, user turns, answers and streams, retrieved chunks, MCP tool lists). Checks: `scanContext`, `checkPrompt`, `scanOutput` / `streamScanner`, `withCanary`, `defineTool` / `authorizeTool`, `checkLimits` / `recordUsage`, `checkMcpTools`, `checkRetrieval`. The platform's policy is an Ed25519-signed JWS fetched every minute in the background. It is applied only when it verifies under a key you pin: `configPublicKey` (a JWK, PEM or JWKS) or `SHIELD360_CONFIG_PUBLIC_KEY`; copy the environment's key from the app's Security tab. Without a pin the checks run in monitor mode with the policy from code. `allowUnpinnedConfig: true` trusts the first key seen, for development only.
649
+
295
650
  ## 🌱 Contributing
296
651
 
297
652
  Whether it's big or small, we love contributions 💚. Check out our [Contribution guide](https://github.com/ThinkfleetAI/shield360-js#readme) to get started
@@ -0,0 +1 @@
1
+ export {};