@dozimple/abap-adt 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.es.md +4 -3
- package/README.md +4 -3
- package/SECURITY.md +12 -11
- package/dist/core/atc.js +2 -1
- package/dist/core/audit.js +81 -12
- package/dist/core/auditkey.js +85 -0
- package/dist/core/config.js +89 -1
- package/dist/core/confirm.js +19 -7
- package/dist/core/connection.js +5 -1
- package/dist/core/credentials.js +10 -3
- package/dist/core/datapolicy.js +15 -7
- package/dist/core/errors.js +20 -6
- package/dist/core/feeds.js +11 -1
- package/dist/core/objects.js +10 -0
- package/dist/core/policy.js +10 -1
- package/dist/core/registry.js +17 -8
- package/dist/index.js +8 -2
- package/dist/scripts/audit-verify.js +25 -6
- package/dist/tools/core/api_release_state.js +5 -0
- package/dist/tools/core/atc_quickfix.js +2 -2
- package/dist/tools/core/ddic_type_info.js +2 -0
- package/dist/tools/core/dumps.js +2 -1
- package/dist/tools/core/gateway_errors.js +2 -1
- package/dist/tools/core/get_source.js +2 -2
- package/dist/tools/core/run_unit_tests.js +2 -0
- package/dist/tools/core/support.js +6 -3
- package/dist/tools/core/table_contents.js +43 -7
- package/dist/tools/core/text_elements.js +11 -3
- package/dist/tools/core/write_source.js +14 -2
- package/dist/tools/docs/docs.js +8 -0
- package/dist/tools/transport-risk/_service.js +7 -2
- package/docs/THREAT_MODEL.md +4 -3
- package/package.json +5 -3
package/dist/tools/docs/docs.js
CHANGED
|
@@ -59,6 +59,14 @@ export function assertDocId(id, cfg) {
|
|
|
59
59
|
catch {
|
|
60
60
|
throw new ToolError("INPUT", `«${id}» no es un id de documento válido.`);
|
|
61
61
|
}
|
|
62
|
+
// La forma se comprueba sobre el id YA decodificado: un «%2F» no puede colar una URL de otro host.
|
|
63
|
+
const shapes = [
|
|
64
|
+
/^sap-help-[A-Za-z0-9_-]+(~[A-Za-z0-9._-]+)?$/,
|
|
65
|
+
/^community-\d+$/,
|
|
66
|
+
/^community-url-https:\/\/community\.sap\.com\/[A-Za-z0-9._~\/?=&#%+-]*$/,
|
|
67
|
+
];
|
|
68
|
+
if (!shapes.some((re) => re.test(decoded)))
|
|
69
|
+
throw new ToolError("INPUT", `«${id}» no tiene la forma de un documento de SAP Help o SAP Community.`);
|
|
62
70
|
assertPublicQuery(decoded.replace(/[-/:.?=&#]/g, " "), cfg, sc.blockTerms);
|
|
63
71
|
return;
|
|
64
72
|
}
|
|
@@ -3,7 +3,7 @@ import axios from "axios";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { tlsOptions } from "../../core/connection.js";
|
|
5
5
|
import { getPassword } from "../../core/credentials.js";
|
|
6
|
-
import { ToolError } from "../../core/errors.js";
|
|
6
|
+
import { sanitizeMessage, ToolError } from "../../core/errors.js";
|
|
7
7
|
/**
|
|
8
8
|
* DoZimple Transport Risk: analizador de riesgo de transportes instalado en el
|
|
9
9
|
* sistema de desarrollo del cliente (nodo ICF /sap/bc/zdz_risk, clase
|
|
@@ -31,6 +31,11 @@ export async function callRiskService(system, params) {
|
|
|
31
31
|
auth: { username: system.user, password: await getPassword(system) },
|
|
32
32
|
httpsAgent: tlsOptions(system).httpsAgent ?? new https.Agent(),
|
|
33
33
|
timeout: 180_000,
|
|
34
|
+
// Un servicio ICF de solo lectura no redirige: sin redirecciones, las credenciales Basic nunca viajan a otra URL
|
|
35
|
+
// (sin depender de que una librería transitiva las quite al cambiar de host).
|
|
36
|
+
maxRedirects: 0,
|
|
37
|
+
// Tope de tamaño de la respuesta antes de procesarla (un sistema averiado no agota la memoria del proceso).
|
|
38
|
+
maxContentLength: 50 * 1024 * 1024,
|
|
34
39
|
// Los 4xx/5xx traen un JSON con la causa: se lee en vez de lanzar.
|
|
35
40
|
validateStatus: () => true,
|
|
36
41
|
responseType: "text",
|
|
@@ -56,7 +61,7 @@ export async function callRiskService(system, params) {
|
|
|
56
61
|
return { text: `El servicio ${RISK_SERVICE_PATH} no existe en este sistema.\n${BACKEND_HINT}`, isError: true };
|
|
57
62
|
if (response.status !== 200) {
|
|
58
63
|
const detail = json && typeof json === "object" && "error" in json ? String(json.error) : body;
|
|
59
|
-
return { text: `El servicio de riesgo respondió HTTP ${response.status}: ${detail}`, isError: true };
|
|
64
|
+
return { text: `El servicio de riesgo respondió HTTP ${response.status}: ${sanitizeMessage(detail, 300)}`, isError: true };
|
|
60
65
|
}
|
|
61
66
|
return body;
|
|
62
67
|
}
|
package/docs/THREAT_MODEL.md
CHANGED
|
@@ -30,8 +30,9 @@ flowchart LR
|
|
|
30
30
|
|---|---|---|---|
|
|
31
31
|
| **S**uplantación | Otro proceso lee la contraseña del llavero | Llavero del SO; `--strict` (opt-in) exige confirmación en cada lectura | Sin `--strict`, un proceso del mismo usuario que invoque `security` puede leerla. Solución de raíz prevista: certificado de cliente / SSO (roadmap) |
|
|
32
32
|
| **S**uplantación | TLS interceptado | Verificación TLS; `caFile` | `allowSelfSigned` sigue existiendo como último recurso, marcado |
|
|
33
|
-
| **T**ampering | Cambiar `systems.json` para habilitar escritura
|
|
34
|
-
| **T**ampering | Retocar el registro de auditoría | Cadena de hash
|
|
33
|
+
| **T**ampering | Cambiar `systems.json` para habilitar escritura, redirigir a otro host o cambiar el comando de un componente (ejecución de código) | Rechazo si es modificable por otros o de otro usuario: permisos en macOS/Linux, ACL por SID en Windows; si no se puede comprobar, no se arranca | Un proceso del propio usuario puede modificarlo |
|
|
34
|
+
| **T**ampering | Retocar el registro de auditoría | Cadena de hash + firma HMAC con clave en el llavero del SO (fecha de creación incluida: quitar firmas se detecta); bloqueo entre procesos; `audit:verify` imprime el último hash para anclarlo fuera | Quien pueda leer el llavero del usuario (su propia sesión, sin `--strict`) podría recalcularlo; el anclaje externo del último hash cubre ese caso. En Windows, sin almacén, solo cadena |
|
|
35
|
+
| **T**ampering | Una escritura aprobada se aplica sobre una versión distinta de la mostrada | La huella de lo mostrado viaja con la confirmación y se compara bajo el bloqueo del objeto; si cambió, no se escribe | — |
|
|
35
36
|
| **R**epudio | «La IA lo cambió sola» o «no hay constancia de ese cambio» | Confirmación humana con vista previa + auditoría con forma de confirmación y huella del contenido. **Fail-closed**: la intención se registra antes de tocar SAP, y si no se puede registrar (disco lleno, permiso denegado, ruta inválida) la escritura no se ejecuta. Si lo que falla es solo el registro del resultado, la respuesta lo avisa explícitamente. Cubierto por tests que simulan ENOSPC y EACCES | El registro es local; un corte de energía entre intención y resultado deja una intención sin resultado, que se interpreta como «estado desconocido, verificar en SAP» |
|
|
36
37
|
| **I**nformation disclosure | Leer hashes, almacén seguro, PSE, OAuth, dumps | Veto directo y a través de vistas y CDS | Tablas Z que copien esos datos no se detectan por nombre |
|
|
37
38
|
| **I**nformation disclosure | Volcar datos personales al proveedor LLM | Clase de datos, enmascarado, tope de filas | Columnas personales con nombres no estándar (Z) no se enmascaran; el tope limita el volumen |
|
|
@@ -44,7 +45,7 @@ flowchart LR
|
|
|
44
45
|
|
|
45
46
|
| Riesgo | Cómo aplica a un MCP de SAP | Control |
|
|
46
47
|
|---|---|---|
|
|
47
|
-
| LLM01 Prompt injection | Comentarios en fuente ABAP, textos, datos de tablas, documentación o posts de comunidad con instrucciones | Marca de «dato, nunca instrucciones» en toda respuesta; ninguna escritura sin confirmación humana
|
|
48
|
+
| LLM01 Prompt injection | Comentarios en fuente ABAP, textos, datos de tablas, documentación o posts de comunidad con instrucciones | Marca de «dato, nunca instrucciones» en toda respuesta (probabilística: viaja en el mismo canal que los datos y el contenido podría imitarla; el protocolo MCP aún no separa instrucciones de datos); marcado neutralizado tras decodificar HTML; **la barrera efectiva**: ninguna escritura sin confirmación humana, tools de escritura destructivas para el cliente, y no existen tools para liberar, importar ni borrar |
|
|
48
49
|
| LLM02 Divulgación de información sensible | Datos personales y material de seguridad en respuestas que llegan al proveedor | Vetos, enmascarado, tope de filas, filtro de salida a internet |
|
|
49
50
|
| LLM03 Cadena de suministro | Dependencias npm, MCP de terceros | Versiones exactas, firmas, SBOM, `ignore-scripts`, terceros aislados tras revisión |
|
|
50
51
|
| LLM05 Manejo inadecuado de la salida | El agente usa una respuesta como si fuera verdad | Errores tipados que nunca parecen vacíos; «tope alcanzado» explícito; calidad de la evidencia en diffs |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dozimple/abap-adt",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "DoZimple MCP server for SAP ABAP: multi-system, secure by design — code review, ATC, diagnosis and documentation for ECC and S/4HANA",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sap",
|
|
@@ -43,11 +43,12 @@
|
|
|
43
43
|
"test": "vitest run",
|
|
44
44
|
"smoke": "node dist/scripts/smoke.js",
|
|
45
45
|
"inspector": "npx @modelcontextprotocol/inspector node dist/index.js",
|
|
46
|
-
"security": "npm audit --omit=dev && npm audit signatures && node scripts/scan-sensitive.mjs",
|
|
46
|
+
"security": "npm audit --omit=dev && npm audit signatures && node scripts/scan-sensitive.mjs && node scripts/scan-sensitive.mjs --history",
|
|
47
47
|
"check": "npm test && npm run build && npm run security",
|
|
48
48
|
"docs": "npm run build && node dist/scripts/gen-docs.js",
|
|
49
49
|
"audit:verify": "node dist/scripts/audit-verify.js",
|
|
50
|
-
"sbom": "npm sbom --omit=dev --sbom-format=cyclonedx --sbom-type=application > sbom.cdx.json"
|
|
50
|
+
"sbom": "npm sbom --omit=dev --sbom-format=cyclonedx --sbom-type=application > sbom.cdx.json",
|
|
51
|
+
"hooks": "git config core.hooksPath .githooks && echo \"hooks activos: pre-commit (código completo) y pre-push (historial)\""
|
|
51
52
|
},
|
|
52
53
|
"engines": {
|
|
53
54
|
"node": ">=22"
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@types/node": "22.20.2",
|
|
64
|
+
"fast-check": "4.10.2",
|
|
63
65
|
"typescript": "5.9.3",
|
|
64
66
|
"vitest": "5.0.0"
|
|
65
67
|
},
|