santismm-knowledge-mcp 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -8
- package/content/matrix/agentic-control-matrix.json +6 -2
- package/content/patterns/correlated-run-trace.json +329 -0
- package/content/patterns/egress-allowlist.json +4 -3
- package/content/patterns/output-boundary-encoding.json +320 -0
- package/content/patterns/recovery-strategy.json +8 -3
- package/dist/shape.js +25 -0
- package/dist/tools.js +27 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,16 +29,34 @@ npm ci && npm run build && npm start
|
|
|
29
29
|
The corpus ships in `content/` and is read from disk, so this works offline.
|
|
30
30
|
Point it elsewhere with `SANTISMM_CONTENT_DIR`.
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## Install from npm
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
```bash
|
|
35
|
+
npx santismm-knowledge-mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or in an MCP client config (stdio):
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{ "mcpServers": { "santismm-knowledge": { "command": "npx", "args": ["santismm-knowledge-mcp"] } } }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The npm package carries the corpus **frozen at publish time**; this repository
|
|
45
|
+
and the hosted endpoint update continuously. When freshness matters, prefer
|
|
46
|
+
the endpoint.
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
## Install from PyPI (Python)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uvx santismm-knowledge-mcp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The Python package (in [`python/`](python/)) is a **zero-dependency stdio
|
|
55
|
+
proxy to the hosted endpoint** — it ships no corpus, so it is always fresh.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{ "mcpServers": { "santismm-knowledge": { "command": "uvx", "args": ["santismm-knowledge-mcp"] } } }
|
|
59
|
+
```
|
|
42
60
|
|
|
43
61
|
|
|
44
62
|
## Licences
|
|
@@ -510,7 +510,9 @@
|
|
|
510
510
|
{
|
|
511
511
|
"id": "ACM-10",
|
|
512
512
|
"group": "observability",
|
|
513
|
-
"patterns": [
|
|
513
|
+
"patterns": [
|
|
514
|
+
"correlated-run-trace"
|
|
515
|
+
],
|
|
514
516
|
"knowledge": [
|
|
515
517
|
"ai-observability"
|
|
516
518
|
],
|
|
@@ -662,7 +664,9 @@
|
|
|
662
664
|
{
|
|
663
665
|
"id": "ACM-13",
|
|
664
666
|
"group": "lifecycle",
|
|
665
|
-
"patterns": [
|
|
667
|
+
"patterns": [
|
|
668
|
+
"output-boundary-encoding"
|
|
669
|
+
],
|
|
666
670
|
"knowledge": [
|
|
667
671
|
"guardrails"
|
|
668
672
|
],
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "correlated-run-trace",
|
|
3
|
+
"category": "reliability",
|
|
4
|
+
"updated": "2026-08-25",
|
|
5
|
+
"version": "1.0",
|
|
6
|
+
"technologies": [
|
|
7
|
+
"OpenTelemetry GenAI semantic conventions",
|
|
8
|
+
"Distributed tracing backends",
|
|
9
|
+
"Tail-based sampling",
|
|
10
|
+
"Field-level redaction at capture",
|
|
11
|
+
"Append-only / WORM storage",
|
|
12
|
+
"Structured logging with a propagated run id"
|
|
13
|
+
],
|
|
14
|
+
"related": [
|
|
15
|
+
"recovery-strategy",
|
|
16
|
+
"evaluator-optimizer",
|
|
17
|
+
"human-escalation",
|
|
18
|
+
"attributed-memory",
|
|
19
|
+
"output-boundary-encoding"
|
|
20
|
+
],
|
|
21
|
+
"references": [
|
|
22
|
+
{
|
|
23
|
+
"title": "OpenTelemetry — semantic conventions for GenAI",
|
|
24
|
+
"url": "https://opentelemetry.io/docs/specs/semconv/gen-ai/"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"title": "EU AI Act — Article 12 (record-keeping)",
|
|
28
|
+
"url": "https://artificialintelligenceact.eu/article/12/"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"title": "EU AI Act — consolidated text",
|
|
32
|
+
"url": "https://eur-lex.europa.eu/eli/reg/2024/1689/oj"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"title": "NIST AI Risk Management Framework",
|
|
36
|
+
"url": "https://www.nist.gov/itl/ai-risk-management-framework"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"title": "OWASP — Top 10 for LLM Applications",
|
|
40
|
+
"url": "https://genai.owasp.org/llm-top-10/"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"evidence": {
|
|
44
|
+
"evidenceLevel": "industry_observation",
|
|
45
|
+
"confidenceLevel": "medium",
|
|
46
|
+
"sourceType": [
|
|
47
|
+
"industry_observation",
|
|
48
|
+
"paper"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"locales": {
|
|
52
|
+
"en": {
|
|
53
|
+
"name": "Correlated Run Trace",
|
|
54
|
+
"summary": "One identifier threads a whole agent run — inputs, model and version, every tool call with its arguments and outcome, the final action — into a record you can replay months later. The hard part is not capture. It is being complete enough to reconstruct the run and restrained enough that the trace store is not a second copy of the data it describes.",
|
|
55
|
+
"definition": "A correlated run trace is an immutable, single-identifier record of everything an agent run decided and did, captured at a fidelity that lets someone reconstruct the run end to end afterwards without access to the systems that produced it, and redacted so that the trace does not become an easier target than the source. It is a record of the agent's decisions, not a request log.",
|
|
56
|
+
"problem": "Agents are non-deterministic and multi-step, so 'what happened' cannot be inferred from the output. Most teams do log, and still cannot answer why a specific run did what it did: the steps are in different systems with no shared identifier, the prompt was recorded as a template reference that has since changed, or the model version was never captured, so nobody can tell whether the behaviour drifted or the model did.",
|
|
57
|
+
"context": "Any agent whose actions have consequences someone will later ask about: a regulated process where the trace is the audit trail and traceability is an obligation rather than a preference, an incident that needs a root cause, a customer disputing an outcome, or an evaluation loop that needs real failures to learn from.",
|
|
58
|
+
"solution": [
|
|
59
|
+
"Mint one run identifier at the entry point and propagate it through every step, service and retry. Correlation is the whole value: unlinked records of the same run are three logs, not a trace.",
|
|
60
|
+
"Record the rendered prompt, not a reference to it. A template id resolves to whatever the template says today, which is not what the model saw.",
|
|
61
|
+
"Capture the model and its version alongside every call. Without it, a behaviour change and a model change are indistinguishable after the fact.",
|
|
62
|
+
"Log tool calls as arguments plus outcome, including the failures and the retries. A trace that shows only successful calls describes a run that did not happen.",
|
|
63
|
+
"Redact at capture, not at read. Field-level rules that drop or hash secrets and personal data before the record is written, because a redaction applied at query time still leaves the raw value in storage.",
|
|
64
|
+
"Sample on the tail, not the head. Decide what to keep after the run finishes, so errors, escalations and anomalies are kept and the routine successes are the ones thinned.",
|
|
65
|
+
"Make the store append-only and give it the access controls of the most sensitive system it describes, not the ones a logging backend ships with."
|
|
66
|
+
],
|
|
67
|
+
"components": [
|
|
68
|
+
"A run identifier minted at entry and propagated through every step, service, retry and asynchronous continuation.",
|
|
69
|
+
"A span per step with a stable schema: inputs, model and version, tool name, arguments, outcome, latency, token usage.",
|
|
70
|
+
"Field-level redaction applied at capture, with an explicit list of what is dropped, what is hashed and what is kept whole.",
|
|
71
|
+
"Tail-based sampling that decides after the fact, so anomalous runs survive and routine ones are thinned.",
|
|
72
|
+
"Append-only storage with retention aligned to the obligation that justifies the record, and access control matching the source systems.",
|
|
73
|
+
"A replay path that reconstructs a run from the trace alone, used often enough that its gaps are known."
|
|
74
|
+
],
|
|
75
|
+
"benefits": [
|
|
76
|
+
"Makes non-deterministic failures diagnosable: you can answer why this run did this, instead of reproducing until it happens again.",
|
|
77
|
+
"Turns record-keeping obligations into an artefact rather than a promise. The reconstruction either works on a random past run or it does not.",
|
|
78
|
+
"Feeds evaluation with real failures instead of invented ones, which is the difference between a benchmark and a regression suite.",
|
|
79
|
+
"Separates drift from deployment. With the model version in the record, 'it got worse' becomes a question with an answer."
|
|
80
|
+
],
|
|
81
|
+
"risks": [
|
|
82
|
+
"The trace store as the softest target: it holds the same data as the systems it describes, usually with weaker access control and longer retention.",
|
|
83
|
+
"Capture cost that grows with traffic until someone samples on the head to save money, quietly removing exactly the runs worth keeping.",
|
|
84
|
+
"Redaction that removes what the reconstruction needed. Over-redaction is invisible until the day someone tries to replay a run and cannot.",
|
|
85
|
+
"Volume mistaken for coverage. Terabytes of spans with no shared identifier still cannot answer a single question about one run."
|
|
86
|
+
],
|
|
87
|
+
"whenNot": [
|
|
88
|
+
"Single-step, deterministic calls where the input and the output are the whole story. A request log already reconstructs those.",
|
|
89
|
+
"Prototypes with no users and no obligations, where the cost of the trace pipeline exceeds anything you would learn from it.",
|
|
90
|
+
"Where the applicable rule forbids retaining the content at all. Then the trace records that a decision happened and its metadata, and the content stays out — that is a different artefact, and pretending otherwise creates the liability the rule was written to avoid."
|
|
91
|
+
],
|
|
92
|
+
"examples": [
|
|
93
|
+
"An incident where an agent emailed the wrong customer. The run identifier links the retrieval that returned the wrong record, the tool call that used it and the message sent, so the root cause is one query rather than a week of reproduction attempts.",
|
|
94
|
+
"A regulator asks how a decision was reached six months ago. The replay path rebuilds the run from the trace alone, including the model version in force that day.",
|
|
95
|
+
"A quality drop after a model upgrade. Because every span carries the model version, the comparison is between two populations of real runs rather than between impressions."
|
|
96
|
+
],
|
|
97
|
+
"kpis": [
|
|
98
|
+
{
|
|
99
|
+
"metric": "Reconstruction success rate",
|
|
100
|
+
"note": "Share of randomly chosen past runs that can be rebuilt end to end from the trace alone. This is the control's own test, and it is the only number here that cannot be satisfied by capturing more."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"metric": "Correlation completeness",
|
|
104
|
+
"note": "Share of spans in a run that carry the run identifier. Anything below 100% means some step is invisible, and the missing one is rarely the boring one."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"metric": "Sensitive-field escape rate",
|
|
108
|
+
"note": "Share of sampled records containing a value the redaction rules should have removed. The target is zero; any other number means the trace store is accumulating a liability."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"metric": "Anomalous-run retention",
|
|
112
|
+
"note": "Share of errored or escalated runs retained after sampling. Head-based sampling drives this toward the sample rate, which is the failure this metric exists to catch."
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"failureModes": [
|
|
116
|
+
"The trace that proves nothing: every step is logged, no step shares an identifier, and reconstructing one run means correlating timestamps by hand.",
|
|
117
|
+
"The prompt recorded by reference. The template changed, so the log now describes a prompt the model never saw, and nobody notices until a reconstruction contradicts the output.",
|
|
118
|
+
"Head-based sampling that keeps the ordinary. The run you need was dropped at the entry point, before anything knew it was going to be interesting.",
|
|
119
|
+
"The log as the breach: raw tool arguments carry personal data into a store with broader access and longer retention than the database they came from.",
|
|
120
|
+
"Missing model version. A behaviour change and a silent model update look identical in the record, and the investigation stalls on a question the trace should have answered."
|
|
121
|
+
],
|
|
122
|
+
"lessons": [
|
|
123
|
+
"Correlation is the product; capture is the raw material. Teams that buy a tracing backend and skip the identifier end up with storage rather than answers.",
|
|
124
|
+
"Test the reconstruction, not the pipeline. Pick a random past run and rebuild it — the gaps are always somewhere nobody instrumented, and only the attempt finds them.",
|
|
125
|
+
"Redact at write time. Every redaction deferred to read time is a decision to keep the raw value, and storage outlives the intention.",
|
|
126
|
+
"Sample on the tail. Head-based sampling is a decision to discard the interesting runs made before anything knows which ones those are.",
|
|
127
|
+
"Record the model version everywhere. It costs a field and it is the difference between diagnosing drift and arguing about it."
|
|
128
|
+
],
|
|
129
|
+
"faqs": [
|
|
130
|
+
{
|
|
131
|
+
"q": "We already use a tracing backend. Isn't this solved?",
|
|
132
|
+
"a": "A backend gives you capture and storage. This pattern is about the three things a backend does not decide for you: whether one identifier threads the whole run, whether the fidelity is enough to reconstruct it without the source systems, and whether what you wrote down is safe to keep. Teams with excellent tooling routinely fail the reconstruction test."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"q": "Doesn't full capture conflict with data minimisation?",
|
|
136
|
+
"a": "It would, if capture meant keeping everything raw. The control states both halves on purpose — enough to reconstruct, nothing that turns the log into the breach — and the way to hold both is field-level redaction at capture plus retention tied to the obligation that justifies the record. What you cannot do is decide the tension away by keeping everything and calling it compliance."
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"q": "How much fidelity is enough?",
|
|
140
|
+
"a": "Exactly enough to pass the reconstruction test on a run picked at random, and no more. That threshold is discoverable by trying it, which is why the test belongs in the routine rather than in an audit. Anything captured beyond it is cost and liability without a question it answers."
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"es": {
|
|
145
|
+
"name": "Traza de ejecución correlada",
|
|
146
|
+
"summary": "Un identificador hilvana la ejecución entera de un agente —entradas, modelo y versión, cada llamada a herramienta con sus argumentos y su resultado, la acción final— en un registro que puedes reproducir meses después. Lo difícil no es capturar. Es ser lo bastante completo para reconstruir la ejecución y lo bastante contenido para que el almacén de trazas no sea una segunda copia de los datos que describe.",
|
|
147
|
+
"definition": "Una traza de ejecución correlada es un registro inmutable, con un único identificador, de todo lo que una ejecución de agente decidió e hizo, capturado con la fidelidad suficiente para que alguien pueda reconstruirla de punta a punta después sin acceso a los sistemas que la produjeron, y redactado de forma que la traza no sea un objetivo más fácil que el origen. Es el registro de decisiones del agente, no un log de peticiones.",
|
|
148
|
+
"problem": "Los agentes son no deterministas y de varios pasos, así que «qué pasó» no se deduce de la salida. Casi todos los equipos registran algo y aun así no pueden explicar por qué una ejecución concreta hizo lo que hizo: los pasos están en sistemas distintos sin identificador común, el prompt se guardó como referencia a una plantilla que ya ha cambiado, o nunca se capturó la versión del modelo, así que nadie puede decir si derivó el comportamiento o cambió el modelo.",
|
|
149
|
+
"context": "Cualquier agente cuyas acciones tengan consecuencias por las que alguien vaya a preguntar después: un proceso regulado donde la traza es el rastro de auditoría y la trazabilidad es una obligación y no una preferencia, un incidente que necesita causa raíz, un cliente que discute un resultado, o un bucle de evaluación que necesita fallos reales de los que aprender.",
|
|
150
|
+
"solution": [
|
|
151
|
+
"Emite un identificador de ejecución en el punto de entrada y propágalo por cada paso, servicio y reintento. La correlación es todo el valor: registros sin enlazar de la misma ejecución son tres logs, no una traza.",
|
|
152
|
+
"Guarda el prompt renderizado, no una referencia a él. Un identificador de plantilla resuelve a lo que la plantilla diga hoy, que no es lo que vio el modelo.",
|
|
153
|
+
"Captura el modelo y su versión junto a cada llamada. Sin eso, un cambio de comportamiento y un cambio de modelo son indistinguibles a posteriori.",
|
|
154
|
+
"Registra las llamadas a herramientas como argumentos más resultado, incluidos los fallos y los reintentos. Una traza que solo enseña las llamadas que salieron bien describe una ejecución que no ocurrió.",
|
|
155
|
+
"Redacta en la captura, no en la lectura. Reglas por campo que descartan o hashean secretos y datos personales antes de escribir el registro, porque una redacción aplicada al consultar deja el valor crudo en el almacenamiento.",
|
|
156
|
+
"Muestrea por la cola, no por la cabeza. Decide qué conservar cuando la ejecución ha terminado, de modo que se guarden los errores, los escalados y las anomalías y se adelgacen los éxitos rutinarios.",
|
|
157
|
+
"Haz el almacén de solo anexado y dale los controles de acceso del sistema más sensible que describe, no los que trae por defecto un backend de logs."
|
|
158
|
+
],
|
|
159
|
+
"components": [
|
|
160
|
+
"Un identificador de ejecución emitido en la entrada y propagado por cada paso, servicio, reintento y continuación asíncrona.",
|
|
161
|
+
"Un span por paso con esquema estable: entradas, modelo y versión, nombre de herramienta, argumentos, resultado, latencia, consumo de tokens.",
|
|
162
|
+
"Redacción por campo aplicada en la captura, con una lista explícita de qué se descarta, qué se hashea y qué se guarda entero.",
|
|
163
|
+
"Muestreo por la cola que decide a posteriori, para que las ejecuciones anómalas sobrevivan y se adelgacen las rutinarias.",
|
|
164
|
+
"Almacenamiento de solo anexado, con retención alineada a la obligación que justifica el registro y control de acceso equivalente al de los sistemas de origen.",
|
|
165
|
+
"Un camino de reproducción que reconstruya una ejecución solo desde la traza, usado con la frecuencia suficiente para que sus huecos se conozcan."
|
|
166
|
+
],
|
|
167
|
+
"benefits": [
|
|
168
|
+
"Hace diagnosticables los fallos no deterministas: puedes responder por qué esta ejecución hizo esto, en vez de reproducir hasta que vuelva a pasar.",
|
|
169
|
+
"Convierte las obligaciones de registro en un artefacto en vez de una promesa. La reconstrucción funciona sobre una ejecución pasada al azar o no funciona.",
|
|
170
|
+
"Alimenta la evaluación con fallos reales en vez de inventados, que es la diferencia entre un benchmark y una suite de regresión.",
|
|
171
|
+
"Separa la deriva del despliegue. Con la versión del modelo en el registro, «ha empeorado» pasa a ser una pregunta con respuesta."
|
|
172
|
+
],
|
|
173
|
+
"risks": [
|
|
174
|
+
"El almacén de trazas como el blanco más blando: guarda los mismos datos que los sistemas que describe, normalmente con menos control de acceso y más retención.",
|
|
175
|
+
"Coste de captura que crece con el tráfico hasta que alguien muestrea por la cabeza para ahorrar, eliminando en silencio justo las ejecuciones que merecía la pena guardar.",
|
|
176
|
+
"Redacción que se lleva por delante lo que la reconstrucción necesitaba. Redactar de más es invisible hasta el día en que alguien intenta reproducir una ejecución y no puede.",
|
|
177
|
+
"Confundir volumen con cobertura. Terabytes de spans sin identificador común siguen sin poder responder ni una pregunta sobre una ejecución."
|
|
178
|
+
],
|
|
179
|
+
"whenNot": [
|
|
180
|
+
"Llamadas deterministas de un solo paso donde la entrada y la salida son toda la historia. Un log de peticiones ya las reconstruye.",
|
|
181
|
+
"Prototipos sin usuarios ni obligaciones, donde el coste del pipeline de trazas supera cualquier cosa que fueras a aprender de él.",
|
|
182
|
+
"Donde la norma aplicable prohíba retener el contenido. Entonces la traza registra que hubo una decisión y sus metadatos, y el contenido se queda fuera: es otro artefacto, y fingir lo contrario crea justo la responsabilidad que la norma quería evitar."
|
|
183
|
+
],
|
|
184
|
+
"examples": [
|
|
185
|
+
"Un incidente en el que un agente escribió al cliente equivocado. El identificador de ejecución enlaza la recuperación que devolvió el registro erróneo, la llamada a herramienta que lo usó y el mensaje enviado, así que la causa raíz es una consulta y no una semana de intentos de reproducción.",
|
|
186
|
+
"Un regulador pregunta cómo se tomó una decisión hace seis meses. El camino de reproducción reconstruye la ejecución solo desde la traza, incluida la versión del modelo vigente aquel día.",
|
|
187
|
+
"Una caída de calidad tras actualizar el modelo. Como cada span lleva la versión, la comparación es entre dos poblaciones de ejecuciones reales y no entre impresiones."
|
|
188
|
+
],
|
|
189
|
+
"kpis": [
|
|
190
|
+
{
|
|
191
|
+
"metric": "Tasa de reconstrucción con éxito",
|
|
192
|
+
"note": "Proporción de ejecuciones pasadas elegidas al azar que se pueden rehacer de punta a punta solo desde la traza. Es la prueba que el propio control define, y el único número de esta lista que no se satisface capturando más."
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"metric": "Completitud de la correlación",
|
|
196
|
+
"note": "Proporción de spans de una ejecución que llevan el identificador. Cualquier cifra por debajo del 100% significa que hay un paso invisible, y el que falta rara vez es el aburrido."
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"metric": "Tasa de fuga de campos sensibles",
|
|
200
|
+
"note": "Proporción de registros muestreados que contienen un valor que las reglas de redacción debían haber quitado. El objetivo es cero; cualquier otra cifra significa que el almacén de trazas está acumulando una responsabilidad."
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"metric": "Retención de ejecuciones anómalas",
|
|
204
|
+
"note": "Proporción de ejecuciones con error o escalado que sobreviven al muestreo. El muestreo por cabeza lleva esta cifra hacia la tasa de muestreo, que es justo el fallo que esta métrica existe para cazar."
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
"failureModes": [
|
|
208
|
+
"La traza que no prueba nada: cada paso está registrado, ninguno comparte identificador, y reconstruir una ejecución significa correlacionar marcas de tiempo a mano.",
|
|
209
|
+
"El prompt guardado por referencia. La plantilla cambió, así que el log describe ahora un prompt que el modelo nunca vio, y nadie se entera hasta que una reconstrucción contradice la salida.",
|
|
210
|
+
"Muestreo por cabeza que se queda lo corriente. La ejecución que necesitas se descartó en el punto de entrada, antes de que nada supiera que iba a ser interesante.",
|
|
211
|
+
"El log como brecha: argumentos de herramienta en crudo llevan datos personales a un almacén con más acceso y más retención que la base de datos de la que salieron.",
|
|
212
|
+
"Falta la versión del modelo. Un cambio de comportamiento y una actualización silenciosa del modelo se ven idénticos en el registro, y la investigación se atasca en una pregunta que la traza debería haber respondido."
|
|
213
|
+
],
|
|
214
|
+
"lessons": [
|
|
215
|
+
"La correlación es el producto; la captura es la materia prima. Los equipos que compran un backend de trazas y se saltan el identificador acaban con almacenamiento, no con respuestas.",
|
|
216
|
+
"Prueba la reconstrucción, no el pipeline. Coge una ejecución pasada al azar y rehazla: los huecos están siempre donde nadie instrumentó, y solo el intento los encuentra.",
|
|
217
|
+
"Redacta al escribir. Cada redacción aplazada a la lectura es una decisión de conservar el valor crudo, y el almacenamiento sobrevive a la intención.",
|
|
218
|
+
"Muestrea por la cola. El muestreo por cabeza es una decisión de tirar las ejecuciones interesantes tomada antes de que nada sepa cuáles son.",
|
|
219
|
+
"Registra la versión del modelo en todas partes. Cuesta un campo y es la diferencia entre diagnosticar la deriva y discutir sobre ella."
|
|
220
|
+
],
|
|
221
|
+
"faqs": [
|
|
222
|
+
{
|
|
223
|
+
"q": "Ya usamos un backend de trazas. ¿No está resuelto?",
|
|
224
|
+
"a": "Un backend te da captura y almacenamiento. Este patrón trata las tres cosas que un backend no decide por ti: si un único identificador hilvana la ejecución entera, si la fidelidad basta para reconstruirla sin los sistemas de origen, y si lo que anotaste es seguro de conservar. Equipos con herramientas excelentes fallan la prueba de reconstrucción con toda normalidad."
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"q": "¿La captura completa no choca con la minimización de datos?",
|
|
228
|
+
"a": "Chocaría si capturar significara guardarlo todo en crudo. El control enuncia las dos mitades a propósito —lo suficiente para reconstruir, nada que convierta el log en la brecha— y la manera de sostener ambas es redacción por campo en la captura más retención atada a la obligación que justifica el registro. Lo que no vale es zanjar la tensión guardándolo todo y llamarlo cumplimiento."
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"q": "¿Cuánta fidelidad es suficiente?",
|
|
232
|
+
"a": "Exactamente la que hace falta para pasar la prueba de reconstrucción sobre una ejecución elegida al azar, y ni una más. Ese umbral se descubre intentándolo, que es la razón de que la prueba pertenezca a la rutina y no a una auditoría. Todo lo capturado por encima es coste y responsabilidad sin una pregunta a la que responda."
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
"pt": {
|
|
237
|
+
"name": "Trace de execução correlacionado",
|
|
238
|
+
"summary": "Um identificador costura a execução inteira de um agente — entradas, modelo e versão, cada chamada de ferramenta com seus argumentos e resultado, a ação final — em um registro que se pode reproduzir meses depois. O difícil não é capturar. É ser completo o bastante para reconstruir a execução e contido o bastante para que o repositório de traces não seja uma segunda cópia dos dados que descreve.",
|
|
239
|
+
"definition": "Um trace de execução correlacionado é um registro imutável, com um único identificador, de tudo o que uma execução de agente decidiu e fez, capturado com fidelidade suficiente para que alguém possa reconstruí-la de ponta a ponta depois, sem acesso aos sistemas que a produziram, e redigido de modo que o trace não seja um alvo mais fácil do que a origem. É o registro de decisões do agente, não um log de requisições.",
|
|
240
|
+
"problem": "Agentes são não determinísticos e de vários passos, então «o que aconteceu» não se deduz da saída. Quase todas as equipes registram algo e ainda assim não conseguem explicar por que uma execução específica fez o que fez: os passos estão em sistemas diferentes sem identificador comum, o prompt foi salvo como referência a um template que já mudou, ou a versão do modelo nunca foi capturada, então ninguém consegue dizer se o comportamento derivou ou se o modelo mudou.",
|
|
241
|
+
"context": "Qualquer agente cujas ações tenham consequências sobre as quais alguém vai perguntar depois: um processo regulado onde o trace é a trilha de auditoria e a rastreabilidade é uma obrigação e não uma preferência, um incidente que precisa de causa raiz, um cliente que contesta um resultado, ou um ciclo de avaliação que precisa de falhas reais para aprender.",
|
|
242
|
+
"solution": [
|
|
243
|
+
"Emita um identificador de execução no ponto de entrada e propague-o por cada passo, serviço e nova tentativa. A correlação é todo o valor: registros desconectados da mesma execução são três logs, não um trace.",
|
|
244
|
+
"Grave o prompt renderizado, não uma referência a ele. Um identificador de template resolve para o que o template diz hoje, que não é o que o modelo viu.",
|
|
245
|
+
"Capture o modelo e sua versão junto de cada chamada. Sem isso, uma mudança de comportamento e uma mudança de modelo são indistinguíveis depois do fato.",
|
|
246
|
+
"Registre as chamadas de ferramenta como argumentos mais resultado, incluindo as falhas e as novas tentativas. Um trace que mostra só as chamadas bem-sucedidas descreve uma execução que não aconteceu.",
|
|
247
|
+
"Redija na captura, não na leitura. Regras por campo que descartam ou aplicam hash a segredos e dados pessoais antes de o registro ser escrito, porque uma redação aplicada na consulta deixa o valor cru no armazenamento.",
|
|
248
|
+
"Amostre pela cauda, não pela cabeça. Decida o que guardar depois que a execução termina, de modo que erros, escalonamentos e anomalias sejam mantidos e os sucessos rotineiros sejam os afinados.",
|
|
249
|
+
"Faça o repositório apenas de acréscimo e dê a ele os controles de acesso do sistema mais sensível que descreve, não os que um backend de logs traz por padrão."
|
|
250
|
+
],
|
|
251
|
+
"components": [
|
|
252
|
+
"Um identificador de execução emitido na entrada e propagado por cada passo, serviço, nova tentativa e continuação assíncrona.",
|
|
253
|
+
"Um span por passo com esquema estável: entradas, modelo e versão, nome da ferramenta, argumentos, resultado, latência, consumo de tokens.",
|
|
254
|
+
"Redação por campo aplicada na captura, com uma lista explícita do que é descartado, do que recebe hash e do que é guardado inteiro.",
|
|
255
|
+
"Amostragem pela cauda que decide depois do fato, para que execuções anômalas sobrevivam e as rotineiras sejam afinadas.",
|
|
256
|
+
"Armazenamento apenas de acréscimo, com retenção alinhada à obrigação que justifica o registro e controle de acesso equivalente ao dos sistemas de origem.",
|
|
257
|
+
"Um caminho de reprodução que reconstrua uma execução só a partir do trace, usado com frequência suficiente para que suas lacunas sejam conhecidas."
|
|
258
|
+
],
|
|
259
|
+
"benefits": [
|
|
260
|
+
"Torna diagnosticáveis as falhas não determinísticas: dá para responder por que esta execução fez isto, em vez de reproduzir até acontecer de novo.",
|
|
261
|
+
"Transforma obrigações de registro em artefato e não em promessa. A reconstrução funciona sobre uma execução passada ao acaso, ou não funciona.",
|
|
262
|
+
"Alimenta a avaliação com falhas reais em vez de inventadas, que é a diferença entre um benchmark e uma suíte de regressão.",
|
|
263
|
+
"Separa a deriva do deploy. Com a versão do modelo no registro, «piorou» vira uma pergunta com resposta."
|
|
264
|
+
],
|
|
265
|
+
"risks": [
|
|
266
|
+
"O repositório de traces como o alvo mais fraco: guarda os mesmos dados dos sistemas que descreve, em geral com menos controle de acesso e mais retenção.",
|
|
267
|
+
"Custo de captura que cresce com o tráfego até alguém amostrar pela cabeça para economizar, removendo em silêncio justamente as execuções que valia a pena guardar.",
|
|
268
|
+
"Redação que leva embora o que a reconstrução precisava. Redigir demais é invisível até o dia em que alguém tenta reproduzir uma execução e não consegue.",
|
|
269
|
+
"Confundir volume com cobertura. Terabytes de spans sem identificador comum continuam sem responder uma única pergunta sobre uma execução."
|
|
270
|
+
],
|
|
271
|
+
"whenNot": [
|
|
272
|
+
"Chamadas determinísticas de um só passo, onde a entrada e a saída são a história inteira. Um log de requisições já as reconstrói.",
|
|
273
|
+
"Protótipos sem usuários e sem obrigações, onde o custo do pipeline de traces supera qualquer coisa que você fosse aprender com ele.",
|
|
274
|
+
"Onde a norma aplicável proíba reter o conteúdo. Então o trace registra que houve uma decisão e seus metadados, e o conteúdo fica de fora: é outro artefato, e fingir o contrário cria exatamente a responsabilidade que a norma queria evitar."
|
|
275
|
+
],
|
|
276
|
+
"examples": [
|
|
277
|
+
"Um incidente em que um agente escreveu ao cliente errado. O identificador de execução liga a recuperação que devolveu o registro errado, a chamada de ferramenta que o usou e a mensagem enviada, então a causa raiz é uma consulta e não uma semana de tentativas de reprodução.",
|
|
278
|
+
"Um regulador pergunta como uma decisão foi tomada seis meses atrás. O caminho de reprodução reconstrói a execução só a partir do trace, incluindo a versão do modelo vigente naquele dia.",
|
|
279
|
+
"Uma queda de qualidade depois de atualizar o modelo. Como cada span carrega a versão, a comparação é entre duas populações de execuções reais e não entre impressões."
|
|
280
|
+
],
|
|
281
|
+
"kpis": [
|
|
282
|
+
{
|
|
283
|
+
"metric": "Taxa de reconstrução bem-sucedida",
|
|
284
|
+
"note": "Proporção de execuções passadas escolhidas ao acaso que podem ser refeitas de ponta a ponta só a partir do trace. É o teste que o próprio controle define, e o único número desta lista que não se satisfaz capturando mais."
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"metric": "Completude da correlação",
|
|
288
|
+
"note": "Proporção de spans de uma execução que carregam o identificador. Qualquer número abaixo de 100% significa que há um passo invisível, e o que falta raramente é o entediante."
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"metric": "Taxa de vazamento de campos sensíveis",
|
|
292
|
+
"note": "Proporção de registros amostrados que contêm um valor que as regras de redação deveriam ter removido. O alvo é zero; qualquer outro número significa que o repositório de traces está acumulando uma responsabilidade."
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"metric": "Retenção de execuções anômalas",
|
|
296
|
+
"note": "Proporção de execuções com erro ou escalonamento que sobrevivem à amostragem. A amostragem pela cabeça leva esse número para perto da taxa de amostragem, que é justamente a falha que esta métrica existe para pegar."
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
"failureModes": [
|
|
300
|
+
"O trace que não prova nada: cada passo está registrado, nenhum compartilha identificador, e reconstruir uma execução significa correlacionar marcas de tempo à mão.",
|
|
301
|
+
"O prompt salvo por referência. O template mudou, então o log agora descreve um prompt que o modelo nunca viu, e ninguém percebe até uma reconstrução contradizer a saída.",
|
|
302
|
+
"Amostragem pela cabeça que fica com o comum. A execução de que você precisa foi descartada no ponto de entrada, antes de qualquer coisa saber que seria interessante.",
|
|
303
|
+
"O log como brecha: argumentos de ferramenta crus levam dados pessoais para um repositório com mais acesso e mais retenção do que o banco de dados de onde saíram.",
|
|
304
|
+
"Falta a versão do modelo. Uma mudança de comportamento e uma atualização silenciosa do modelo parecem idênticas no registro, e a investigação empaca numa pergunta que o trace deveria ter respondido."
|
|
305
|
+
],
|
|
306
|
+
"lessons": [
|
|
307
|
+
"A correlação é o produto; a captura é a matéria-prima. Equipes que compram um backend de traces e pulam o identificador acabam com armazenamento, não com respostas.",
|
|
308
|
+
"Teste a reconstrução, não o pipeline. Pegue uma execução passada ao acaso e refaça-a: as lacunas estão sempre onde ninguém instrumentou, e só a tentativa as encontra.",
|
|
309
|
+
"Redija na escrita. Toda redação adiada para a leitura é uma decisão de conservar o valor cru, e o armazenamento sobrevive à intenção.",
|
|
310
|
+
"Amostre pela cauda. A amostragem pela cabeça é uma decisão de jogar fora as execuções interessantes tomada antes de qualquer coisa saber quais são.",
|
|
311
|
+
"Registre a versão do modelo em todo lugar. Custa um campo e é a diferença entre diagnosticar a deriva e discutir sobre ela."
|
|
312
|
+
],
|
|
313
|
+
"faqs": [
|
|
314
|
+
{
|
|
315
|
+
"q": "Já usamos um backend de traces. Isso não está resolvido?",
|
|
316
|
+
"a": "Um backend dá captura e armazenamento. Este padrão trata das três coisas que um backend não decide por você: se um único identificador costura a execução inteira, se a fidelidade basta para reconstruí-la sem os sistemas de origem, e se o que você anotou é seguro de guardar. Equipes com ferramentas excelentes falham o teste de reconstrução com toda a naturalidade."
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"q": "A captura completa não conflita com a minimização de dados?",
|
|
320
|
+
"a": "Conflitaria, se capturar significasse guardar tudo cru. O controle enuncia as duas metades de propósito — o suficiente para reconstruir, nada que transforme o log na brecha — e a maneira de sustentar ambas é redação por campo na captura mais retenção atada à obrigação que justifica o registro. O que não vale é resolver a tensão guardando tudo e chamando isso de conformidade."
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"q": "Quanta fidelidade é suficiente?",
|
|
324
|
+
"a": "Exatamente a necessária para passar no teste de reconstrução sobre uma execução escolhida ao acaso, e nada além. Esse limiar se descobre tentando, e é por isso que o teste pertence à rotina e não a uma auditoria. Tudo o que for capturado além disso é custo e responsabilidade sem uma pergunta a que responda."
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"slug": "egress-allowlist",
|
|
3
3
|
"category": "safety",
|
|
4
|
-
"updated": "2026-08-
|
|
5
|
-
"version": "1.
|
|
4
|
+
"updated": "2026-08-25",
|
|
5
|
+
"version": "1.1",
|
|
6
6
|
"technologies": [
|
|
7
7
|
"Egress proxies",
|
|
8
8
|
"Container network policies",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"related": [
|
|
14
14
|
"least-privilege-tooling",
|
|
15
15
|
"human-approval-gate",
|
|
16
|
-
"recovery-strategy"
|
|
16
|
+
"recovery-strategy",
|
|
17
|
+
"output-boundary-encoding"
|
|
17
18
|
],
|
|
18
19
|
"references": [
|
|
19
20
|
{
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "output-boundary-encoding",
|
|
3
|
+
"category": "safety",
|
|
4
|
+
"updated": "2026-08-25",
|
|
5
|
+
"version": "1.0",
|
|
6
|
+
"technologies": [
|
|
7
|
+
"Context-aware output encoders",
|
|
8
|
+
"Markdown renderers with raw HTML disabled",
|
|
9
|
+
"Parameterised queries and ORM bindings",
|
|
10
|
+
"argv-array process execution",
|
|
11
|
+
"URL and egress allowlists",
|
|
12
|
+
"JSON Schema validation of tool-call arguments"
|
|
13
|
+
],
|
|
14
|
+
"related": [
|
|
15
|
+
"egress-allowlist",
|
|
16
|
+
"least-privilege-tooling",
|
|
17
|
+
"sandboxed-execution",
|
|
18
|
+
"human-approval-gate",
|
|
19
|
+
"attributed-memory"
|
|
20
|
+
],
|
|
21
|
+
"references": [
|
|
22
|
+
{
|
|
23
|
+
"title": "OWASP — Top 10 for LLM Applications",
|
|
24
|
+
"url": "https://genai.owasp.org/llm-top-10/"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"title": "OWASP LLM01 — Prompt Injection (the paired risk)",
|
|
28
|
+
"url": "https://genai.owasp.org/llmrisk/llm01-prompt-injection/"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"title": "MITRE ATLAS — adversarial techniques against AI systems",
|
|
32
|
+
"url": "https://atlas.mitre.org/"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"title": "NIST AI Risk Management Framework",
|
|
36
|
+
"url": "https://www.nist.gov/itl/ai-risk-management-framework"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"title": "EU AI Act — Article 15 (accuracy, robustness and cybersecurity)",
|
|
40
|
+
"url": "https://artificialintelligenceact.eu/article/15/"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"evidence": {
|
|
44
|
+
"evidenceLevel": "industry_observation",
|
|
45
|
+
"confidenceLevel": "medium",
|
|
46
|
+
"sourceType": [
|
|
47
|
+
"industry_observation",
|
|
48
|
+
"paper"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"locales": {
|
|
52
|
+
"en": {
|
|
53
|
+
"name": "Output Boundary Encoding",
|
|
54
|
+
"summary": "Treat everything the model emits as hostile input to whatever consumes it. Encode and validate at each destination — renderer, shell, query, downstream agent — using that destination's own rules. One global sanitiser cannot do this: escaping that is correct for HTML is meaningless for a shell.",
|
|
55
|
+
"definition": "Output boundary encoding is the practice of applying destination-specific encoding and validation to model output at every point where it crosses into a system that will interpret it, rather than filtering it once, generically, on the way out of the model. Its absence is the weakness OWASP names as improper output handling.",
|
|
56
|
+
"problem": "Teams harden the input side against prompt injection and leave the output side open. The model's text then reaches a renderer, a shell, a database or another agent's context, where it is interpreted as instruction rather than displayed as data. The attacker never has to reach the model directly.",
|
|
57
|
+
"context": "Any agent whose output reaches something that parses it: a chat interface rendering markdown, a coding agent running a suggested command, a tool call built from generated arguments, a summary fed into a second agent's prompt, or a webhook that forwards the text onward.",
|
|
58
|
+
"solution": [
|
|
59
|
+
"Enumerate the destinations before writing any filter. Every place model output lands and is interpreted — HTML renderer, shell, query, file path, URL fetcher, another agent's context, a downstream webhook — is a distinct boundary with distinct rules.",
|
|
60
|
+
"Encode at the destination, not at the source. HTML-escape for the renderer, parameterise for the query, pass an argv array to the process. The encoding belongs where the interpretation happens, because only there do you know what will be interpreted.",
|
|
61
|
+
"Prefer structured output over prose you have to parse back. A tool call with typed arguments has a schema to validate against; a sentence you regex for a filename does not.",
|
|
62
|
+
"Validate the value, not only the syntax. An encoded path is still a path: check it resolves inside the directory you meant before opening it.",
|
|
63
|
+
"Treat outbound URLs as a destination of their own. Rendered images and links cause a fetch with no click, so they carry data out; allowlist the hosts a rendered link may reach.",
|
|
64
|
+
"Make the boundary the only route. If any code path can consume raw model output without passing a destination encoder, the control is advisory rather than real."
|
|
65
|
+
],
|
|
66
|
+
"components": [
|
|
67
|
+
"A destination inventory: every consumer of model output, with the encoder each one requires.",
|
|
68
|
+
"Per-destination encoders — HTML, process argv, query parameters, path resolution, URL allowlist — rather than one shared sanitiser.",
|
|
69
|
+
"Schema-validated structured output for tool calls, so arguments are typed rather than parsed out of prose.",
|
|
70
|
+
"An egress allowlist covering the hosts reachable from rendered links and images.",
|
|
71
|
+
"Contract tests that emit a payload per destination and assert it is neutralised at the boundary.",
|
|
72
|
+
"Logging when an encoder neutralises something, so the boundary can tell you it is on the path."
|
|
73
|
+
],
|
|
74
|
+
"benefits": [
|
|
75
|
+
"Breaks the injection chain where it matters: even a fully persuaded model cannot make a downstream system act, because that system never interprets its text.",
|
|
76
|
+
"Independent of model behaviour. It keeps working across model upgrades, new jailbreaks and prompt changes, because it does not depend on the model refusing anything.",
|
|
77
|
+
"Testable. Each destination has a payload and a pass or fail, so the control produces evidence rather than assurance.",
|
|
78
|
+
"Cheap when applied early. Adding an encoder is a boundary change; retrofitting one after the destination is everywhere is a refactor."
|
|
79
|
+
],
|
|
80
|
+
"risks": [
|
|
81
|
+
"One sanitiser for every destination. It feels like a control, satisfies the checklist, and is wrong at every boundary except the one it was written for.",
|
|
82
|
+
"Encoding that breaks the product: over-escaping turns legitimate markdown, code blocks and non-Latin text into noise, and the pressure to loosen it lands on the encoder rather than on the destination list.",
|
|
83
|
+
"A destination inventory that ages. New integrations add consumers, and nothing fails when one is missed.",
|
|
84
|
+
"Confusing detection with encoding. Scanning output for suspicious strings catches last year's payloads; encoding does not need to recognise the attack at all."
|
|
85
|
+
],
|
|
86
|
+
"whenNot": [
|
|
87
|
+
"Output that is never interpreted — a score, an enum, a boolean the caller compares. Constrain the type instead; an encoder on a closed value set is ceremony.",
|
|
88
|
+
"Fully local single-user tools with no rendering and no process execution, where the only consumer is a person reading text.",
|
|
89
|
+
"Where the destination already parameterises by construction, such as an ORM binding or a template engine that escapes by default. A second encoder gains nothing and can double-encode."
|
|
90
|
+
],
|
|
91
|
+
"examples": [
|
|
92
|
+
"A support agent summarises a ticket whose body contains a markdown image pointing at an attacker's host. The console renders it, the browser fetches the URL, and the conversation leaks with nobody clicking anything. Disabling raw HTML and allowlisting image hosts closes it.",
|
|
93
|
+
"A coding agent proposes a shell command. The runner passes the string to a shell, so a filename containing a command separator executes. Passing an argv array instead removes the shell's parsing step entirely.",
|
|
94
|
+
"One agent's summary is placed into a second agent's prompt. The summary contained instructions, and the second agent followed them. Fencing the untrusted span and labelling it as data is the boundary in that case."
|
|
95
|
+
],
|
|
96
|
+
"kpis": [
|
|
97
|
+
{
|
|
98
|
+
"metric": "Destination coverage",
|
|
99
|
+
"note": "Share of known model-output consumers with an encoder at the boundary. Below 100% the control has a specific hole, and naming the destination is more useful than a percentage that averages it away."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"metric": "Payload neutralisation rate",
|
|
103
|
+
"note": "Share of per-destination test payloads neutralised at the boundary. The target is 100%: anything else names a destination to fix rather than a number to improve."
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"metric": "Time to cover a new destination",
|
|
107
|
+
"note": "How long from a new integration going live to its encoder existing. Measures whether the inventory keeps up with the product rather than whether it was right once."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"metric": "Boundary trigger volume",
|
|
111
|
+
"note": "How often encoders neutralise something in production. A flat zero usually means the encoder is not on the path, not that nothing hostile is arriving."
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"failureModes": [
|
|
115
|
+
"Silent exfiltration through rendered markup: an image or link causes a fetch without a click, so data leaves with no user action and no error anyone would notice.",
|
|
116
|
+
"Second-order injection: output encoded correctly for the console is stored and later rendered somewhere else — a log viewer, a ticket, a digest email — where that encoding does not apply.",
|
|
117
|
+
"The encoder that only sits on the happy path. Error branches, retries and fallbacks emit the same text through a different code path with nothing on it.",
|
|
118
|
+
"Double encoding. Two layers each escape correctly, users see escaped entities in the product, and the fix removes the wrong layer."
|
|
119
|
+
],
|
|
120
|
+
"lessons": [
|
|
121
|
+
"Enumerate destinations before writing filters. Almost every real failure here is a consumer nobody listed, not an encoder that was written wrong.",
|
|
122
|
+
"The destination you forget is rarely a screen. It is a webhook, a log viewer, an export or a digest email — somewhere the output goes without anyone thinking of it as a render.",
|
|
123
|
+
"Encoding beats detection because encoding does not need to recognise the attack. A payload blocklist is a description of the attacks that were already public.",
|
|
124
|
+
"Do not let a single sanitise() become the answer. The name suggests completeness and the behaviour is correct for exactly one destination."
|
|
125
|
+
],
|
|
126
|
+
"faqs": [
|
|
127
|
+
{
|
|
128
|
+
"q": "Isn't this the same as filtering inputs for prompt injection?",
|
|
129
|
+
"a": "No — they defend opposite ends. Input filtering tries to stop the model from being persuaded, which depends on the model. This assumes persuasion already succeeded and stops the output from being acted on, which does not depend on the model at all. A system with only the first fails the moment a new jailbreak appears."
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"q": "Isn't this just output sanitization? Why not one sanitiser for everything?",
|
|
133
|
+
"a": "Because encoding is contextual. Escaping a quote protects an HTML renderer and does nothing for a shell; shell-quoting protects a shell and corrupts displayed text. A shared function has to choose one context, is wrong in the others, and looks like coverage while being a single point of failure."
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"q": "The model is ours and the prompt is fixed. Do we still need this?",
|
|
137
|
+
"a": "Yes, if any content the model reads comes from outside: a fetched page, a user file, a tool result, a memory record. The instruction does not have to arrive through your prompt — it arrives through whatever the model reads, and your prompt being fixed does not constrain that."
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"es": {
|
|
142
|
+
"name": "Codificación en la frontera de salida",
|
|
143
|
+
"summary": "Trata todo lo que el modelo emite como entrada hostil para quien lo consuma. Codifica y valida en cada destino —renderizador, shell, consulta, agente aguas abajo— con las reglas de ese destino. Un saneador global no puede hacerlo: el escapado correcto para HTML no significa nada para un shell.",
|
|
144
|
+
"definition": "La codificación en la frontera de salida es la práctica de aplicar codificación y validación específicas de cada destino a la salida del modelo en todo punto donde cruza hacia un sistema que va a interpretarla, en lugar de filtrarla una sola vez, de forma genérica, al salir del modelo. Su ausencia es la debilidad que OWASP llama improper output handling: manejo indebido de la salida.",
|
|
145
|
+
"problem": "Los equipos endurecen el lado de la entrada contra la inyección de prompts y dejan abierto el de la salida. El texto del modelo llega entonces a un renderizador, un shell, una base de datos o el contexto de otro agente, donde se interpreta como instrucción en vez de mostrarse como dato. El atacante no necesita llegar al modelo directamente. Dicho de otro modo: la salida del modelo hay que tratarla como entrada no confiable.",
|
|
146
|
+
"context": "Cualquier agente cuya salida llegue a algo que la parsea: una interfaz de chat que renderiza markdown, un agente de código que ejecuta un comando sugerido, una llamada a herramienta construida con argumentos generados, un resumen que se inyecta en el prompt de un segundo agente, o un webhook que reenvía el texto.",
|
|
147
|
+
"solution": [
|
|
148
|
+
"Enumera los destinos antes de escribir ningún filtro. Cada sitio donde aterriza la salida del modelo y se interpreta —renderizador HTML, shell, consulta, ruta de fichero, descargador de URLs, contexto de otro agente, webhook aguas abajo— es una frontera distinta con reglas distintas.",
|
|
149
|
+
"Codifica en el destino, no en el origen. Escapa HTML para el renderizador, parametriza la consulta, pasa un array argv al proceso. La codificación pertenece a donde ocurre la interpretación, porque solo ahí sabes qué se va a interpretar.",
|
|
150
|
+
"Prefiere salida estructurada antes que prosa que luego hay que volver a parsear. Una llamada a herramienta con argumentos tipados tiene un esquema contra el que validar; una frase a la que le aplicas una expresión regular para sacar un nombre de fichero, no.",
|
|
151
|
+
"Valida el valor, no solo la sintaxis. Una ruta codificada sigue siendo una ruta: comprueba que resuelve dentro del directorio que querías antes de abrirla.",
|
|
152
|
+
"Trata las URLs salientes como un destino propio. Las imágenes y los enlaces renderizados provocan una petición sin que nadie pulse, así que sacan datos; pon en lista de permitidos los hosts que puede alcanzar un enlace renderizado.",
|
|
153
|
+
"Haz que la frontera sea el único camino. Si alguna ruta de código puede consumir salida cruda del modelo sin pasar por un codificador de destino, el control es orientativo, no real."
|
|
154
|
+
],
|
|
155
|
+
"components": [
|
|
156
|
+
"Un inventario de destinos: cada consumidor de la salida del modelo, con el codificador que necesita cada uno.",
|
|
157
|
+
"Codificadores por destino —HTML, argv de proceso, parámetros de consulta, resolución de rutas, lista de URLs permitidas— en lugar de un saneador compartido.",
|
|
158
|
+
"Salida estructurada validada contra esquema para las llamadas a herramientas, de modo que los argumentos estén tipados y no extraídos de la prosa.",
|
|
159
|
+
"Una lista de egreso permitido que cubra los hosts alcanzables desde enlaces e imágenes renderizados.",
|
|
160
|
+
"Pruebas de contrato que emiten un payload por destino y comprueban que se neutraliza en la frontera.",
|
|
161
|
+
"Registro de cuándo un codificador neutraliza algo, para que la frontera pueda decirte que está en el camino."
|
|
162
|
+
],
|
|
163
|
+
"benefits": [
|
|
164
|
+
"Rompe la cadena de inyección donde importa: ni un modelo completamente persuadido puede hacer que un sistema aguas abajo actúe, porque ese sistema nunca interpreta su texto.",
|
|
165
|
+
"Independiente del comportamiento del modelo. Sigue funcionando entre versiones, jailbreaks nuevos y cambios de prompt, porque no depende de que el modelo se niegue a nada.",
|
|
166
|
+
"Comprobable. Cada destino tiene un payload y un resultado de pasa o falla, así que el control produce evidencia en vez de garantías.",
|
|
167
|
+
"Barato si se aplica pronto. Añadir un codificador es un cambio de frontera; ponerlo cuando el destino ya está por todas partes es una refactorización."
|
|
168
|
+
],
|
|
169
|
+
"risks": [
|
|
170
|
+
"Un solo saneador para todos los destinos. Parece un control, satisface la lista de verificación y está mal en todas las fronteras menos en aquella para la que se escribió.",
|
|
171
|
+
"Codificación que rompe el producto: escapar de más convierte markdown legítimo, bloques de código y texto no latino en ruido, y la presión por aflojar recae sobre el codificador en vez de sobre la lista de destinos.",
|
|
172
|
+
"Un inventario de destinos que envejece. Cada integración nueva añade consumidores, y no falla nada cuando se olvida uno.",
|
|
173
|
+
"Confundir detección con codificación. Escanear la salida buscando cadenas sospechosas caza los payloads del año pasado; la codificación no necesita reconocer el ataque."
|
|
174
|
+
],
|
|
175
|
+
"whenNot": [
|
|
176
|
+
"Salida que nunca se interpreta: una puntuación, un enum, un booleano que el llamante compara. Restringe el tipo en su lugar; un codificador sobre un conjunto cerrado de valores es ceremonia.",
|
|
177
|
+
"Herramientas locales de un solo usuario, sin renderizado ni ejecución de procesos, donde el único consumidor es una persona leyendo texto.",
|
|
178
|
+
"Donde el destino ya parametriza por construcción, como el binding de un ORM o un motor de plantillas que escapa por defecto. Un segundo codificador no aporta nada y puede provocar doble codificación."
|
|
179
|
+
],
|
|
180
|
+
"examples": [
|
|
181
|
+
"Un agente de soporte resume un ticket cuyo cuerpo contiene una imagen markdown apuntando al host de un atacante. La consola la renderiza, el navegador pide la URL, y la conversación se filtra sin que nadie pulse nada. Deshabilitar HTML crudo y permitir solo ciertos hosts de imagen lo cierra.",
|
|
182
|
+
"Un agente de código propone un comando de shell. El ejecutor pasa la cadena a un shell, así que un nombre de fichero con un separador de comandos se ejecuta. Pasar un array argv elimina por completo el paso de parseo del shell.",
|
|
183
|
+
"El resumen de un agente se coloca en el prompt de un segundo agente. El resumen contenía instrucciones y el segundo las siguió. Delimitar el fragmento no confiable y etiquetarlo como dato es la frontera en ese caso."
|
|
184
|
+
],
|
|
185
|
+
"kpis": [
|
|
186
|
+
{
|
|
187
|
+
"metric": "Cobertura de destinos",
|
|
188
|
+
"note": "Proporción de consumidores conocidos de la salida del modelo con codificador en la frontera. Por debajo del 100% el control tiene un agujero concreto, y nombrar el destino sirve más que un porcentaje que lo promedia."
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"metric": "Tasa de neutralización de payloads",
|
|
192
|
+
"note": "Proporción de payloads de prueba por destino neutralizados en la frontera. El objetivo es 100%: cualquier otra cifra nombra un destino que arreglar, no un número que mejorar."
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"metric": "Tiempo hasta cubrir un destino nuevo",
|
|
196
|
+
"note": "Cuánto pasa desde que una integración entra en producción hasta que existe su codificador. Mide si el inventario sigue el ritmo del producto, no si acertó una vez."
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"metric": "Volumen de disparos en la frontera",
|
|
200
|
+
"note": "Con qué frecuencia los codificadores neutralizan algo en producción. Un cero plano suele significar que el codificador no está en el camino, no que no llegue nada hostil."
|
|
201
|
+
}
|
|
202
|
+
],
|
|
203
|
+
"failureModes": [
|
|
204
|
+
"Exfiltración silenciosa por marcado renderizado: una imagen o un enlace provocan una petición sin pulsar, así que los datos salen sin acción del usuario y sin ningún error que alguien fuera a notar.",
|
|
205
|
+
"Inyección de segundo orden: la salida se codifica bien para la consola, se almacena y luego se renderiza en otro sitio —un visor de logs, un ticket, un correo resumen— donde esa codificación no aplica.",
|
|
206
|
+
"El codificador que solo está en el camino feliz. Las ramas de error, los reintentos y los repliegues emiten el mismo texto por otra ruta de código que no lleva nada.",
|
|
207
|
+
"Doble codificación. Dos capas escapan correctamente, los usuarios ven entidades escapadas en el producto, y el arreglo quita la capa equivocada."
|
|
208
|
+
],
|
|
209
|
+
"lessons": [
|
|
210
|
+
"Enumera destinos antes de escribir filtros. Casi todo fallo real aquí es un consumidor que nadie listó, no un codificador mal escrito.",
|
|
211
|
+
"El destino que se olvida rara vez es una pantalla. Es un webhook, un visor de logs, una exportación o un correo resumen: algún sitio al que va la salida sin que nadie lo piense como un renderizado.",
|
|
212
|
+
"La codificación gana a la detección porque no necesita reconocer el ataque. Una lista negra de payloads es una descripción de los ataques que ya eran públicos.",
|
|
213
|
+
"No dejes que un único sanitize() se convierta en la respuesta. El nombre sugiere completitud y el comportamiento es correcto para exactamente un destino."
|
|
214
|
+
],
|
|
215
|
+
"faqs": [
|
|
216
|
+
{
|
|
217
|
+
"q": "¿No es lo mismo que filtrar entradas contra la inyección de prompts?",
|
|
218
|
+
"a": "No: defienden extremos opuestos. El filtrado de entrada intenta que no persuadan al modelo, y eso depende del modelo. Esto asume que ya lo persuadieron e impide que se actúe sobre la salida, y eso no depende del modelo en absoluto. Un sistema que solo tenga lo primero falla en cuanto aparece un jailbreak nuevo."
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"q": "¿Esto no es simplemente sanitization de la salida, con un saneador único para todo?",
|
|
222
|
+
"a": "Porque la codificación es contextual. Escapar una comilla protege un renderizador HTML y no hace nada por un shell; entrecomillar para shell protege el shell y corrompe el texto mostrado. Una función compartida tiene que elegir un contexto, está mal en los demás, y parece cobertura mientras es un punto único de fallo."
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"q": "El modelo es nuestro y el prompt está fijado. ¿Aun así hace falta?",
|
|
226
|
+
"a": "Sí, si algo de lo que el modelo lee viene de fuera: una página descargada, un fichero del usuario, el resultado de una herramienta, un registro de memoria. La instrucción no tiene que llegar por tu prompt: llega por lo que el modelo lea, y que tu prompt esté fijado no restringe eso."
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
"pt": {
|
|
231
|
+
"name": "Codificação na fronteira de saída",
|
|
232
|
+
"summary": "Trate tudo o que o modelo emite como entrada hostil para quem o consome. Codifique e valide em cada destino — renderizador, shell, consulta, agente a jusante — com as regras desse destino. Um sanitizador global não dá conta: o escape correto para HTML não significa nada para um shell.",
|
|
233
|
+
"definition": "A codificação na fronteira de saída é a prática de aplicar codificação e validação específicas de cada destino à saída do modelo em todo ponto onde ela cruza para um sistema que vai interpretá-la, em vez de filtrá-la uma única vez, de forma genérica, ao sair do modelo. Sua ausência é a fraqueza que a OWASP chama de improper output handling: tratamento indevido da saída.",
|
|
234
|
+
"problem": "As equipes endurecem o lado da entrada contra injeção de prompt e deixam o lado da saída aberto. O texto do modelo então chega a um renderizador, a um shell, a um banco de dados ou ao contexto de outro agente, onde é interpretado como instrução em vez de exibido como dado. O atacante nunca precisa alcançar o modelo diretamente. Dito de outro modo: a saída do modelo tem de ser tratada como entrada não confiável.",
|
|
235
|
+
"context": "Qualquer agente cuja saída chegue a algo que a analisa: uma interface de chat que renderiza markdown, um agente de código que executa um comando sugerido, uma chamada de ferramenta construída com argumentos gerados, um resumo injetado no prompt de um segundo agente, ou um webhook que encaminha o texto adiante.",
|
|
236
|
+
"solution": [
|
|
237
|
+
"Enumere os destinos antes de escrever qualquer filtro. Cada lugar onde a saída do modelo aterrissa e é interpretada — renderizador HTML, shell, consulta, caminho de arquivo, buscador de URLs, contexto de outro agente, webhook a jusante — é uma fronteira distinta com regras distintas.",
|
|
238
|
+
"Codifique no destino, não na origem. Escape HTML para o renderizador, parametrize a consulta, passe um array argv ao processo. A codificação pertence ao lugar onde ocorre a interpretação, porque só ali se sabe o que será interpretado.",
|
|
239
|
+
"Prefira saída estruturada a prosa que depois é preciso analisar de volta. Uma chamada de ferramenta com argumentos tipados tem um esquema contra o qual validar; uma frase da qual você extrai um nome de arquivo com expressão regular, não.",
|
|
240
|
+
"Valide o valor, não só a sintaxe. Um caminho codificado continua sendo um caminho: verifique que ele resolve dentro do diretório pretendido antes de abri-lo.",
|
|
241
|
+
"Trate as URLs de saída como um destino próprio. Imagens e links renderizados provocam uma requisição sem clique, portanto levam dados para fora; permita apenas os hosts que um link renderizado pode alcançar.",
|
|
242
|
+
"Faça da fronteira o único caminho. Se alguma rota de código puder consumir saída crua do modelo sem passar por um codificador de destino, o controle é orientativo, não real."
|
|
243
|
+
],
|
|
244
|
+
"components": [
|
|
245
|
+
"Um inventário de destinos: cada consumidor da saída do modelo, com o codificador exigido por cada um.",
|
|
246
|
+
"Codificadores por destino — HTML, argv de processo, parâmetros de consulta, resolução de caminhos, lista de URLs permitidas — em vez de um sanitizador compartilhado.",
|
|
247
|
+
"Saída estruturada validada por esquema para as chamadas de ferramentas, de modo que os argumentos sejam tipados e não extraídos da prosa.",
|
|
248
|
+
"Uma lista de egresso permitido cobrindo os hosts alcançáveis a partir de links e imagens renderizados.",
|
|
249
|
+
"Testes de contrato que emitem um payload por destino e verificam que ele é neutralizado na fronteira.",
|
|
250
|
+
"Registro de quando um codificador neutraliza algo, para que a fronteira consiga dizer que está no caminho."
|
|
251
|
+
],
|
|
252
|
+
"benefits": [
|
|
253
|
+
"Rompe a cadeia de injeção onde importa: nem um modelo completamente persuadido consegue fazer um sistema a jusante agir, porque esse sistema nunca interpreta o texto dele.",
|
|
254
|
+
"Independente do comportamento do modelo. Continua funcionando entre versões, novos jailbreaks e mudanças de prompt, porque não depende de o modelo recusar nada.",
|
|
255
|
+
"Verificável. Cada destino tem um payload e um resultado de passa ou falha, então o controle produz evidência em vez de garantia.",
|
|
256
|
+
"Barato quando aplicado cedo. Adicionar um codificador é uma mudança de fronteira; colocá-lo depois que o destino já está em toda parte é uma refatoração."
|
|
257
|
+
],
|
|
258
|
+
"risks": [
|
|
259
|
+
"Um único sanitizador para todos os destinos. Parece um controle, satisfaz a lista de verificação e está errado em todas as fronteiras menos naquela para a qual foi escrito.",
|
|
260
|
+
"Codificação que quebra o produto: escapar demais transforma markdown legítimo, blocos de código e texto não latino em ruído, e a pressão por afrouxar recai sobre o codificador em vez de sobre a lista de destinos.",
|
|
261
|
+
"Um inventário de destinos que envelhece. Cada nova integração acrescenta consumidores, e nada falha quando um é esquecido.",
|
|
262
|
+
"Confundir detecção com codificação. Varrer a saída em busca de cadeias suspeitas pega os payloads do ano passado; a codificação não precisa reconhecer o ataque."
|
|
263
|
+
],
|
|
264
|
+
"whenNot": [
|
|
265
|
+
"Saída que nunca é interpretada: uma pontuação, um enum, um booleano que o chamador compara. Restrinja o tipo em vez disso; um codificador sobre um conjunto fechado de valores é cerimônia.",
|
|
266
|
+
"Ferramentas locais de um só usuário, sem renderização e sem execução de processos, onde o único consumidor é uma pessoa lendo texto.",
|
|
267
|
+
"Onde o destino já parametriza por construção, como o binding de um ORM ou um motor de templates que escapa por padrão. Um segundo codificador não acrescenta nada e pode causar dupla codificação."
|
|
268
|
+
],
|
|
269
|
+
"examples": [
|
|
270
|
+
"Um agente de suporte resume um chamado cujo corpo contém uma imagem markdown apontando para o host de um atacante. O console a renderiza, o navegador busca a URL, e a conversa vaza sem que ninguém clique em nada. Desabilitar HTML cru e permitir apenas certos hosts de imagem fecha isso.",
|
|
271
|
+
"Um agente de código propõe um comando de shell. O executor passa a string a um shell, então um nome de arquivo com um separador de comandos é executado. Passar um array argv elimina por completo a etapa de análise do shell.",
|
|
272
|
+
"O resumo de um agente é colocado no prompt de um segundo agente. O resumo continha instruções e o segundo as seguiu. Delimitar o trecho não confiável e rotulá-lo como dado é a fronteira nesse caso."
|
|
273
|
+
],
|
|
274
|
+
"kpis": [
|
|
275
|
+
{
|
|
276
|
+
"metric": "Cobertura de destinos",
|
|
277
|
+
"note": "Proporção de consumidores conhecidos da saída do modelo com codificador na fronteira. Abaixo de 100% o controle tem um buraco concreto, e nomear o destino serve mais do que uma porcentagem que o dilui na média."
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"metric": "Taxa de neutralização de payloads",
|
|
281
|
+
"note": "Proporção de payloads de teste por destino neutralizados na fronteira. O alvo é 100%: qualquer outro número nomeia um destino a corrigir, não um número a melhorar."
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"metric": "Tempo até cobrir um novo destino",
|
|
285
|
+
"note": "Quanto tempo passa desde que uma integração entra em produção até que o codificador dela exista. Mede se o inventário acompanha o produto, não se ele acertou uma vez."
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"metric": "Volume de disparos na fronteira",
|
|
289
|
+
"note": "Com que frequência os codificadores neutralizam algo em produção. Um zero constante costuma significar que o codificador não está no caminho, não que nada hostil esteja chegando."
|
|
290
|
+
}
|
|
291
|
+
],
|
|
292
|
+
"failureModes": [
|
|
293
|
+
"Exfiltração silenciosa por marcação renderizada: uma imagem ou um link provocam uma requisição sem clique, então os dados saem sem ação do usuário e sem nenhum erro que alguém fosse notar.",
|
|
294
|
+
"Injeção de segunda ordem: a saída é codificada corretamente para o console, é armazenada e depois renderizada em outro lugar — um visualizador de logs, um chamado, um e-mail de resumo — onde essa codificação não se aplica.",
|
|
295
|
+
"O codificador que só está no caminho feliz. Ramos de erro, novas tentativas e fallbacks emitem o mesmo texto por outra rota de código, sem nada nela.",
|
|
296
|
+
"Dupla codificação. Duas camadas escapam corretamente, os usuários veem entidades escapadas no produto, e a correção remove a camada errada."
|
|
297
|
+
],
|
|
298
|
+
"lessons": [
|
|
299
|
+
"Enumere destinos antes de escrever filtros. Quase toda falha real aqui é um consumidor que ninguém listou, não um codificador escrito errado.",
|
|
300
|
+
"O destino que se esquece raramente é uma tela. É um webhook, um visualizador de logs, uma exportação ou um e-mail de resumo: algum lugar para onde a saída vai sem que ninguém o pense como renderização.",
|
|
301
|
+
"A codificação vence a detecção porque não precisa reconhecer o ataque. Uma lista de bloqueio de payloads é uma descrição dos ataques que já eram públicos.",
|
|
302
|
+
"Não deixe um único sanitize() virar a resposta. O nome sugere completude e o comportamento está correto para exatamente um destino."
|
|
303
|
+
],
|
|
304
|
+
"faqs": [
|
|
305
|
+
{
|
|
306
|
+
"q": "Isso não é o mesmo que filtrar entradas contra injeção de prompt?",
|
|
307
|
+
"a": "Não: defendem extremos opostos. O filtro de entrada tenta impedir que o modelo seja persuadido, e isso depende do modelo. Isto aqui assume que a persuasão já ocorreu e impede que se aja sobre a saída, e isso não depende do modelo em nada. Um sistema que só tenha o primeiro falha assim que surge um novo jailbreak."
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"q": "Isto não é simplesmente sanitization da saída, com um sanitizador único para tudo?",
|
|
311
|
+
"a": "Porque a codificação é contextual. Escapar uma aspa protege um renderizador HTML e não faz nada por um shell; aspas de shell protegem o shell e corrompem o texto exibido. Uma função compartilhada precisa escolher um contexto, está errada nos demais, e parece cobertura enquanto é um ponto único de falha."
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"q": "O modelo é nosso e o prompt é fixo. Ainda assim é preciso?",
|
|
315
|
+
"a": "Sim, se algo que o modelo lê vem de fora: uma página buscada, um arquivo do usuário, o resultado de uma ferramenta, um registro de memória. A instrução não precisa chegar pelo seu prompt: chega por aquilo que o modelo lê, e o seu prompt ser fixo não restringe isso."
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"slug": "recovery-strategy",
|
|
3
3
|
"category": "reliability",
|
|
4
|
-
"updated": "2026-
|
|
4
|
+
"updated": "2026-08-25",
|
|
5
5
|
"version": "1.1",
|
|
6
6
|
"featured": false,
|
|
7
7
|
"technologies": [
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"related": [
|
|
14
14
|
"reflection",
|
|
15
15
|
"human-escalation",
|
|
16
|
-
"evaluator-optimizer"
|
|
16
|
+
"evaluator-optimizer",
|
|
17
|
+
"correlated-run-trace"
|
|
17
18
|
],
|
|
18
19
|
"references": [
|
|
19
20
|
{
|
|
@@ -28,7 +29,11 @@
|
|
|
28
29
|
"evidence": {
|
|
29
30
|
"evidenceLevel": "production",
|
|
30
31
|
"confidenceLevel": "low",
|
|
31
|
-
"sourceType": [
|
|
32
|
+
"sourceType": [
|
|
33
|
+
"production_system",
|
|
34
|
+
"personal_experience",
|
|
35
|
+
"industry_observation"
|
|
36
|
+
]
|
|
32
37
|
},
|
|
33
38
|
"locales": {
|
|
34
39
|
"en": {
|
package/dist/shape.js
CHANGED
|
@@ -78,6 +78,22 @@ const DOMAIN_INFO = {
|
|
|
78
78
|
lookup: "HRN id (e.g. HRN-001) or slug",
|
|
79
79
|
},
|
|
80
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Newest `updated` across a set of units, or null if none carries one.
|
|
83
|
+
*
|
|
84
|
+
* Dates are ISO `YYYY-MM-DD`, so a string comparison is the date comparison.
|
|
85
|
+
* Derived from the loaded content rather than stamped at build time, so it is
|
|
86
|
+
* correct in every deployment without anyone remembering to update it.
|
|
87
|
+
*/
|
|
88
|
+
function newestUpdated(items) {
|
|
89
|
+
let newest = null;
|
|
90
|
+
for (const it of items) {
|
|
91
|
+
const u = it.updated;
|
|
92
|
+
if (typeof u === "string" && (newest === null || u > newest))
|
|
93
|
+
newest = u;
|
|
94
|
+
}
|
|
95
|
+
return newest;
|
|
96
|
+
}
|
|
81
97
|
/** Distinct, sorted category values — read off the content, never hand-listed. */
|
|
82
98
|
function categoriesOf(items) {
|
|
83
99
|
return [...new Set(items.map((e) => e.category).filter((c) => Boolean(c)))].sort();
|
|
@@ -463,6 +479,15 @@ export function makeContent(loadAll, loadHandbook, loadHomeric) {
|
|
|
463
479
|
license_url: LICENSE_INFO.url,
|
|
464
480
|
total: domains.reduce((n, d) => n + d.count, 0),
|
|
465
481
|
domains,
|
|
482
|
+
corpus: {
|
|
483
|
+
newest_unit: newestUpdated([
|
|
484
|
+
...DOMAINS.flatMap((d) => loadAll(d)),
|
|
485
|
+
...(loadHandbook ? loadHandbook() : []),
|
|
486
|
+
]),
|
|
487
|
+
freshness: `This describes the copy answering the call. ${SITE_URL}/mcp is redeployed on ` +
|
|
488
|
+
"every content change; a published package carries the corpus frozen at publish " +
|
|
489
|
+
"time. If total or newest_unit differ from that endpoint's, you are holding a snapshot.",
|
|
490
|
+
},
|
|
466
491
|
next: "search(query, locale) to answer a question across the whole corpus; " +
|
|
467
492
|
"list_<domain> to browse one; get_<domain>(slug) for a full unit with its " +
|
|
468
493
|
"Evidence-First provenance; get_related(domain, slug) to traverse the graph. " +
|
package/dist/tools.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
* between them. The data source is injected as `content` (an `McpContent`
|
|
8
8
|
* provider); both providers read the same `content/{domain}/*.json` files.
|
|
9
9
|
*/
|
|
10
|
-
export const SERVER_INFO = { name: "santismm-knowledge", version: "0.2.
|
|
10
|
+
export const SERVER_INFO = { name: "santismm-knowledge", version: "0.2.2" };
|
|
11
11
|
/**
|
|
12
12
|
* Every tool here reads a static corpus and nothing else, so all four hints are
|
|
13
13
|
* literally true rather than aspirational: nothing mutates, the same arguments
|
|
@@ -349,6 +349,26 @@ function noEncontrado(content, domain, pedido, locale) {
|
|
|
349
349
|
pistas.push(`${espacio.listTool} returns every identifier this tool accepts.`);
|
|
350
350
|
}
|
|
351
351
|
pistas.push("Identifiers are not interchangeable between spaces; do not invent one by analogy.");
|
|
352
|
+
/**
|
|
353
|
+
* The third reason a lookup fails, and the one this answer could not express.
|
|
354
|
+
*
|
|
355
|
+
* A published package carries the corpus frozen at publish time, so an agent
|
|
356
|
+
* holding one gets `not_found` for a unit that exists — and everything above
|
|
357
|
+
* tells it, truthfully but misleadingly, that the identifier is not in the
|
|
358
|
+
* corpus. Stating the date of this copy turns that into something the caller
|
|
359
|
+
* can check instead of a dead end wearing a helpful face.
|
|
360
|
+
*/
|
|
361
|
+
try {
|
|
362
|
+
const fecha = content.overview().corpus.newest_unit;
|
|
363
|
+
if (fecha) {
|
|
364
|
+
cuerpo.corpus_newest_unit = fecha;
|
|
365
|
+
pistas.push(`This copy holds nothing newer than ${fecha}. If the unit was added after that, ` +
|
|
366
|
+
"a package snapshot is the reason and the hosted endpoint has it.");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
// La recuperación nunca puede ser la que rompa la respuesta.
|
|
371
|
+
}
|
|
352
372
|
cuerpo.hint = pistas.join(" ");
|
|
353
373
|
return {
|
|
354
374
|
content: [{ type: "text", text: JSON.stringify(cuerpo, null, 2) }],
|
|
@@ -421,6 +441,12 @@ export function registerTools(server, content) {
|
|
|
421
441
|
url: z.string(),
|
|
422
442
|
api_url: z.string(),
|
|
423
443
|
})),
|
|
444
|
+
corpus: z
|
|
445
|
+
.object({
|
|
446
|
+
newest_unit: z.string().nullable().describe("Newest unit date in THIS copy (YYYY-MM-DD)."),
|
|
447
|
+
freshness: z.string(),
|
|
448
|
+
})
|
|
449
|
+
.describe("Whether this copy is current. Compare against the hosted endpoint: a lower total or an older newest_unit means you are holding a snapshot, not that the corpus lacks what you asked for."),
|
|
424
450
|
next: z.string(),
|
|
425
451
|
bulk: z.record(z.string(), z.string()),
|
|
426
452
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "santismm-knowledge-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "MCP server for the Santismm Knowledge Platform — harness engineering, agentic AI patterns, reference architectures and AI governance. Ships the corpus; the hosted endpoint at https://santismm.com/mcp is the always-fresh alternative.",
|
|
6
6
|
"type": "module",
|