arc-1 1.1.2 → 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.
- package/README.md +9 -1
- package/bin/start-cf.sh +32 -0
- package/dist/adt/bounded-response.d.ts +9 -0
- package/dist/adt/bounded-response.d.ts.map +1 -0
- package/dist/adt/bounded-response.js +216 -0
- package/dist/adt/bounded-response.js.map +1 -0
- package/dist/adt/client.d.ts +35 -18
- package/dist/adt/client.d.ts.map +1 -1
- package/dist/adt/client.js +100 -104
- package/dist/adt/client.js.map +1 -1
- package/dist/adt/config.d.ts +6 -0
- package/dist/adt/config.d.ts.map +1 -1
- package/dist/adt/config.js +3 -0
- package/dist/adt/config.js.map +1 -1
- package/dist/adt/data-result-context.d.ts +46 -0
- package/dist/adt/data-result-context.d.ts.map +1 -0
- package/dist/adt/data-result-context.js +118 -0
- package/dist/adt/data-result-context.js.map +1 -0
- package/dist/adt/data-source-name.d.ts +60 -0
- package/dist/adt/data-source-name.d.ts.map +1 -0
- package/dist/adt/data-source-name.js +156 -0
- package/dist/adt/data-source-name.js.map +1 -0
- package/dist/adt/data-source-policy.d.ts +148 -0
- package/dist/adt/data-source-policy.d.ts.map +1 -0
- package/dist/adt/data-source-policy.js +752 -0
- package/dist/adt/data-source-policy.js.map +1 -0
- package/dist/adt/errors.d.ts +10 -0
- package/dist/adt/errors.d.ts.map +1 -1
- package/dist/adt/errors.js +17 -0
- package/dist/adt/errors.js.map +1 -1
- package/dist/adt/http-deadline.d.ts +5 -0
- package/dist/adt/http-deadline.d.ts.map +1 -1
- package/dist/adt/http-deadline.js +7 -0
- package/dist/adt/http-deadline.js.map +1 -1
- package/dist/adt/http.d.ts.map +1 -1
- package/dist/adt/http.js +43 -46
- package/dist/adt/http.js.map +1 -1
- package/dist/adt/internal-data-operations.d.ts +51 -0
- package/dist/adt/internal-data-operations.d.ts.map +1 -0
- package/dist/adt/internal-data-operations.js +95 -0
- package/dist/adt/internal-data-operations.js.map +1 -0
- package/dist/adt/safety.d.ts +2 -0
- package/dist/adt/safety.d.ts.map +1 -1
- package/dist/adt/safety.js +6 -0
- package/dist/adt/safety.js.map +1 -1
- package/dist/adt/sql-source-analyzer.d.ts +12 -0
- package/dist/adt/sql-source-analyzer.d.ts.map +1 -0
- package/dist/adt/sql-source-analyzer.js +158 -0
- package/dist/adt/sql-source-analyzer.js.map +1 -0
- package/dist/adt/table-query.d.ts +41 -0
- package/dist/adt/table-query.d.ts.map +1 -0
- package/dist/adt/table-query.js +99 -0
- package/dist/adt/table-query.js.map +1 -0
- package/dist/adt/xml-parser.d.ts +10 -15
- package/dist/adt/xml-parser.d.ts.map +1 -1
- package/dist/adt/xml-parser.js +24 -11
- package/dist/adt/xml-parser.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/handlers/diagnose.d.ts.map +1 -1
- package/dist/handlers/diagnose.js +14 -1
- package/dist/handlers/diagnose.js.map +1 -1
- package/dist/handlers/dispatch.d.ts +3 -1
- package/dist/handlers/dispatch.d.ts.map +1 -1
- package/dist/handlers/dispatch.js +74 -6
- package/dist/handlers/dispatch.js.map +1 -1
- package/dist/handlers/navigate.d.ts.map +1 -1
- package/dist/handlers/navigate.js +23 -3
- package/dist/handlers/navigate.js.map +1 -1
- package/dist/handlers/object-types.d.ts +4 -4
- package/dist/handlers/object-types.js +4 -4
- package/dist/handlers/query.d.ts +1 -1
- package/dist/handlers/query.d.ts.map +1 -1
- package/dist/handlers/query.js +13 -20
- package/dist/handlers/query.js.map +1 -1
- package/dist/handlers/read.d.ts.map +1 -1
- package/dist/handlers/read.js +68 -12
- package/dist/handlers/read.js.map +1 -1
- package/dist/handlers/search.d.ts.map +1 -1
- package/dist/handlers/search.js +27 -5
- package/dist/handlers/search.js.map +1 -1
- package/dist/handlers/tools.d.ts.map +1 -1
- package/dist/handlers/tools.js +13 -13
- package/dist/handlers/tools.js.map +1 -1
- package/dist/handlers/where-used.d.ts +2 -0
- package/dist/handlers/where-used.d.ts.map +1 -1
- package/dist/handlers/where-used.js +23 -5
- package/dist/handlers/where-used.js.map +1 -1
- package/dist/public/index.d.ts +1 -1
- package/dist/public/index.d.ts.map +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/index.js.map +1 -1
- package/dist/server/audit.d.ts +42 -1
- package/dist/server/audit.d.ts.map +1 -1
- package/dist/server/audit.js.map +1 -1
- package/dist/server/config.d.ts +4 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +67 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/context.d.ts +5 -0
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js.map +1 -1
- package/dist/server/destination-registry.d.ts +10 -2
- package/dist/server/destination-registry.d.ts.map +1 -1
- package/dist/server/destination-registry.js +12 -3
- package/dist/server/destination-registry.js.map +1 -1
- package/dist/server/effective-policy-log.d.ts +2 -1
- package/dist/server/effective-policy-log.d.ts.map +1 -1
- package/dist/server/effective-policy-log.js +18 -2
- package/dist/server/effective-policy-log.js.map +1 -1
- package/dist/server/multi-target-runtime.d.ts.map +1 -1
- package/dist/server/multi-target-runtime.js +7 -2
- package/dist/server/multi-target-runtime.js.map +1 -1
- package/dist/server/runtime-memory.d.ts +13 -0
- package/dist/server/runtime-memory.d.ts.map +1 -0
- package/dist/server/runtime-memory.js +42 -0
- package/dist/server/runtime-memory.js.map +1 -0
- package/dist/server/server-instructions.d.ts +2 -0
- package/dist/server/server-instructions.d.ts.map +1 -0
- package/dist/server/server-instructions.js +27 -0
- package/dist/server/server-instructions.js.map +1 -0
- package/dist/server/server.d.ts +3 -2
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +25 -35
- package/dist/server/server.js.map +1 -1
- package/dist/server/sinks/btp-auditlog.d.ts.map +1 -1
- package/dist/server/sinks/btp-auditlog.js +10 -0
- package/dist/server/sinks/btp-auditlog.js.map +1 -1
- package/dist/server/types.d.ts +8 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +4 -0
- package/dist/server/types.js.map +1 -1
- package/dist/server/ui-state.d.ts.map +1 -1
- package/dist/server/ui-state.js +1 -0
- package/dist/server/ui-state.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { AdtResponseLimitError } from './errors.js';
|
|
2
|
+
export const DEFAULT_DATA_PREVIEW_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
3
|
+
export const DEFAULT_CONCURRENT_DATA_RESULTS = 2;
|
|
4
|
+
/** One cumulative post-content-decoding allowance shared by a complete MCP tool call. */
|
|
5
|
+
export class DataResponseBudget {
|
|
6
|
+
limitBytes;
|
|
7
|
+
endpointFamily;
|
|
8
|
+
committedBytes = 0;
|
|
9
|
+
inFlightBytes = 0;
|
|
10
|
+
constructor(limitBytes, endpointFamily = 'data-preview') {
|
|
11
|
+
this.limitBytes = limitBytes;
|
|
12
|
+
this.endpointFamily = endpointFamily;
|
|
13
|
+
if (!Number.isSafeInteger(limitBytes) || limitBytes < 1) {
|
|
14
|
+
throw new RangeError(`Data response limit must be a positive safe integer, got ${limitBytes}.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
get consumedBytes() {
|
|
18
|
+
return this.committedBytes;
|
|
19
|
+
}
|
|
20
|
+
get reservedBytes() {
|
|
21
|
+
return this.inFlightBytes;
|
|
22
|
+
}
|
|
23
|
+
createReservation() {
|
|
24
|
+
return new DataResponseReservation(this, { bytes: 0, settled: false });
|
|
25
|
+
}
|
|
26
|
+
assertSingleResponseBytes(bytes, requestId) {
|
|
27
|
+
if (bytes > this.limitBytes) {
|
|
28
|
+
throw new AdtResponseLimitError(this.limitBytes, bytes, this.endpointFamily, requestId);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
reserve(state, nextChunkBytes, requestId) {
|
|
32
|
+
if (state.settled)
|
|
33
|
+
throw new Error('Cannot reserve bytes on a settled data-response reservation.');
|
|
34
|
+
const observedBytes = this.committedBytes + this.inFlightBytes + nextChunkBytes;
|
|
35
|
+
if (observedBytes > this.limitBytes) {
|
|
36
|
+
throw new AdtResponseLimitError(this.limitBytes, observedBytes, this.endpointFamily, requestId);
|
|
37
|
+
}
|
|
38
|
+
state.bytes += nextChunkBytes;
|
|
39
|
+
this.inFlightBytes += nextChunkBytes;
|
|
40
|
+
}
|
|
41
|
+
commit(state) {
|
|
42
|
+
if (state.settled)
|
|
43
|
+
return;
|
|
44
|
+
state.settled = true;
|
|
45
|
+
this.inFlightBytes -= state.bytes;
|
|
46
|
+
this.committedBytes += state.bytes;
|
|
47
|
+
}
|
|
48
|
+
release(state) {
|
|
49
|
+
if (state.settled)
|
|
50
|
+
return;
|
|
51
|
+
state.settled = true;
|
|
52
|
+
this.inFlightBytes -= state.bytes;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class DataResponseReservation {
|
|
56
|
+
budget;
|
|
57
|
+
state;
|
|
58
|
+
constructor(budget, state) {
|
|
59
|
+
this.budget = budget;
|
|
60
|
+
this.state = state;
|
|
61
|
+
}
|
|
62
|
+
add(bytes, requestId) {
|
|
63
|
+
this.budget.reserve(this.state, bytes, requestId);
|
|
64
|
+
}
|
|
65
|
+
commit() {
|
|
66
|
+
this.budget.commit(this.state);
|
|
67
|
+
}
|
|
68
|
+
release() {
|
|
69
|
+
this.budget.release(this.state);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Lazy process-wide admission lease plus the request's cumulative response budget. */
|
|
73
|
+
export class DataResultScope {
|
|
74
|
+
semaphore;
|
|
75
|
+
responseBudget;
|
|
76
|
+
leasePromise;
|
|
77
|
+
leaseHeld = false;
|
|
78
|
+
released = false;
|
|
79
|
+
_queueWaitMs = 0;
|
|
80
|
+
constructor(limitBytes, semaphore) {
|
|
81
|
+
this.semaphore = semaphore;
|
|
82
|
+
this.responseBudget = new DataResponseBudget(limitBytes);
|
|
83
|
+
}
|
|
84
|
+
get queueWaitMs() {
|
|
85
|
+
return this._queueWaitMs;
|
|
86
|
+
}
|
|
87
|
+
get hasLease() {
|
|
88
|
+
return this.leaseHeld;
|
|
89
|
+
}
|
|
90
|
+
async acquire(signal) {
|
|
91
|
+
if (this.released)
|
|
92
|
+
throw new Error('Cannot acquire a released data-result scope.');
|
|
93
|
+
if (!this.semaphore || this.leaseHeld)
|
|
94
|
+
return;
|
|
95
|
+
if (!this.leasePromise) {
|
|
96
|
+
const startedAt = Date.now();
|
|
97
|
+
this.leasePromise = this.semaphore.acquire(signal).then(() => {
|
|
98
|
+
this._queueWaitMs = Date.now() - startedAt;
|
|
99
|
+
if (this.released) {
|
|
100
|
+
this.semaphore?.release();
|
|
101
|
+
throw new Error('Data-result scope was released while waiting for admission.');
|
|
102
|
+
}
|
|
103
|
+
this.leaseHeld = true;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
await this.leasePromise;
|
|
107
|
+
}
|
|
108
|
+
release() {
|
|
109
|
+
if (this.released)
|
|
110
|
+
return;
|
|
111
|
+
this.released = true;
|
|
112
|
+
if (this.leaseHeld) {
|
|
113
|
+
this.leaseHeld = false;
|
|
114
|
+
this.semaphore?.release();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=data-result-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-result-context.js","sourceRoot":"","sources":["../../src/adt/data-result-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical ARC-1 data-source identity.
|
|
3
|
+
*
|
|
4
|
+
* This is the SINGLE normalizer for every value that can take part in a data-source policy
|
|
5
|
+
* decision: configuration entries, caller-supplied roots, SQL parser results, repository-search
|
|
6
|
+
* results, URI-decoded DDLS identities, dependency-graph node names and aliases, replacement-object
|
|
7
|
+
* annotations, and internal registry entries. Two different normalizers would let the name that is
|
|
8
|
+
* checked drift from the name that is executed, which is the whole class of bug this module exists
|
|
9
|
+
* to make impossible.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately dependency-free (besides `node:crypto`) so `src/server/config.ts` can validate
|
|
12
|
+
* startup configuration without pulling the ABAP parser into process start.
|
|
13
|
+
*/
|
|
14
|
+
/** Upper bound on a canonical technical name. */
|
|
15
|
+
export declare const MAX_DATA_SOURCE_NAME_LENGTH = 128;
|
|
16
|
+
/** A value could not be canonicalized into an exact ABAP data-source identity. */
|
|
17
|
+
export declare class DataSourceNameError extends Error {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
/** Strip only ASCII surrounding whitespace. Unicode whitespace is intentionally preserved. */
|
|
21
|
+
export declare function asciiTrim(raw: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Canonicalize one exact ABAP data-source identity, or throw.
|
|
24
|
+
*
|
|
25
|
+
* Order matters and is part of the contract: ASCII-trim, then validate the RAW value, then fold
|
|
26
|
+
* case. Nothing is ever silently removed — a name that would need character stripping is rejected.
|
|
27
|
+
*
|
|
28
|
+
* @param raw the value as supplied by configuration, a caller, or SAP metadata
|
|
29
|
+
* @param what short label used in the error message (for example `SAP_BLOCKED_DATA_SOURCES entry #2`)
|
|
30
|
+
*/
|
|
31
|
+
export declare function canonicalDataSourceName(raw: string, what?: string): string;
|
|
32
|
+
/** Canonicalize a list, dropping exact duplicates while keeping first-occurrence order. */
|
|
33
|
+
export declare function canonicalDataSourceNames(raws: readonly string[], what?: string): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Parse the `SAP_BLOCKED_DATA_SOURCES` CSV contract.
|
|
36
|
+
*
|
|
37
|
+
* - unset (caller passes `undefined`), empty, or ASCII-whitespace-only → off (empty list);
|
|
38
|
+
* - once the ASCII-trimmed value is non-empty, EVERY comma-separated field is mandatory, so
|
|
39
|
+
* `,` `,,,` `,USR02` `USR02,` and `USR02,,PA0002` are startup errors rather than silently dropped;
|
|
40
|
+
* - each field is canonicalized with {@link canonicalDataSourceName};
|
|
41
|
+
* - duplicates collapse while preserving first-occurrence order.
|
|
42
|
+
*
|
|
43
|
+
* A value consisting only of NON-ASCII whitespace (for example U+3000) is deliberately an error, not
|
|
44
|
+
* "off": it is indistinguishable from a corrupted deployment value, and only ASCII blanks are the
|
|
45
|
+
* documented off switch.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseBlockedDataSourcesCsv(raw: string, sourceLabel?: string): string[];
|
|
48
|
+
/**
|
|
49
|
+
* Deterministic policy fingerprint: SHA-256 over `data-source-blocklist:v1\n` followed by the
|
|
50
|
+
* sorted, unique, canonical names joined with `\n`.
|
|
51
|
+
*
|
|
52
|
+
* Order- and duplicate-independent, so two deployments with the same effective policy produce the
|
|
53
|
+
* same value. This is a configuration-drift and correlation identifier ONLY — it is unsalted by
|
|
54
|
+
* design, the candidate name space is small and guessable, and it must never be described as
|
|
55
|
+
* protecting the contents of the list. Exact values live on administrator-only surfaces.
|
|
56
|
+
*/
|
|
57
|
+
export declare function dataSourcePolicyFingerprint(names: readonly string[]): string;
|
|
58
|
+
/** Short human-facing form of {@link dataSourcePolicyFingerprint} for startup logs. */
|
|
59
|
+
export declare function shortPolicyFingerprint(names: readonly string[]): string;
|
|
60
|
+
//# sourceMappingURL=data-source-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source-name.d.ts","sourceRoot":"","sources":["../../src/adt/data-source-name.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,iDAAiD;AACjD,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAqB/C,kFAAkF;AAClF,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,8FAA8F;AAC9F,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAqB,GAAG,MAAM,CAoBtF;AAED,2FAA2F;AAC3F,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,SAAqB,GAAG,MAAM,EAAE,CAWrG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,SAA6B,GAAG,MAAM,EAAE,CAqB1G;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK5E;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEvE"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical ARC-1 data-source identity.
|
|
3
|
+
*
|
|
4
|
+
* This is the SINGLE normalizer for every value that can take part in a data-source policy
|
|
5
|
+
* decision: configuration entries, caller-supplied roots, SQL parser results, repository-search
|
|
6
|
+
* results, URI-decoded DDLS identities, dependency-graph node names and aliases, replacement-object
|
|
7
|
+
* annotations, and internal registry entries. Two different normalizers would let the name that is
|
|
8
|
+
* checked drift from the name that is executed, which is the whole class of bug this module exists
|
|
9
|
+
* to make impossible.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately dependency-free (besides `node:crypto`) so `src/server/config.ts` can validate
|
|
12
|
+
* startup configuration without pulling the ABAP parser into process start.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
15
|
+
/** Upper bound on a canonical technical name. */
|
|
16
|
+
export const MAX_DATA_SOURCE_NAME_LENGTH = 128;
|
|
17
|
+
/**
|
|
18
|
+
* ASCII whitespace only — space, tab, LF, VT, FF, CR.
|
|
19
|
+
*
|
|
20
|
+
* `String.prototype.trim()` also strips Unicode whitespace (U+00A0, U+3000, U+2028, …). That is
|
|
21
|
+
* exactly what we must NOT do: trimming a non-ASCII pad would let `" USR02"` canonicalize to
|
|
22
|
+
* `USR02`, i.e. two visually different configuration values collapsing to one identity. Unicode
|
|
23
|
+
* whitespace is not trimmed here, so it survives into the ASCII check below and is rejected.
|
|
24
|
+
*/
|
|
25
|
+
const ASCII_TRIM = /^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g;
|
|
26
|
+
/** The complete accepted character set, tested against the RAW value before any case folding. */
|
|
27
|
+
const ASCII_ALLOWED = /^[A-Za-z0-9_/$]+$/;
|
|
28
|
+
/** At least one letter or digit: `/`, `$`, `_`, and combinations of them are not names. */
|
|
29
|
+
const HAS_ALPHANUMERIC = /[A-Za-z0-9]/;
|
|
30
|
+
/** Longest offending token echoed back in a startup error. */
|
|
31
|
+
const MAX_ECHOED_TOKEN = 64;
|
|
32
|
+
/** A value could not be canonicalized into an exact ABAP data-source identity. */
|
|
33
|
+
export class DataSourceNameError extends Error {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'DataSourceNameError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Strip only ASCII surrounding whitespace. Unicode whitespace is intentionally preserved. */
|
|
40
|
+
export function asciiTrim(raw) {
|
|
41
|
+
return raw.replace(ASCII_TRIM, '');
|
|
42
|
+
}
|
|
43
|
+
/** Echo an operator's bad token without dumping unbounded or unrelated content. */
|
|
44
|
+
function echoToken(raw) {
|
|
45
|
+
const flat = raw.replace(/[\r\n\t]/g, ' ');
|
|
46
|
+
const clipped = flat.length > MAX_ECHOED_TOKEN ? `${flat.slice(0, MAX_ECHOED_TOKEN)}…` : flat;
|
|
47
|
+
return JSON.stringify(clipped);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Uppercase ASCII `a-z` and nothing else.
|
|
51
|
+
*
|
|
52
|
+
* `String.prototype.toUpperCase()` is Unicode-aware: `ſ` (U+017F) folds to `S`, `ß` to `SS`, and the
|
|
53
|
+
* `ff` ligature to `FF`. Applying it before validation makes a validator that claims to accept only
|
|
54
|
+
* `A-Z0-9_/$` silently accept non-ASCII input. The ASCII gate above already rejects those inputs, so
|
|
55
|
+
* this mapping is defence in depth: it stays correct even if the checks are ever reordered.
|
|
56
|
+
*/
|
|
57
|
+
function upperCaseAscii(value) {
|
|
58
|
+
let out = '';
|
|
59
|
+
for (let i = 0; i < value.length; i++) {
|
|
60
|
+
const code = value.charCodeAt(i);
|
|
61
|
+
out += code >= 97 && code <= 122 ? String.fromCharCode(code - 32) : value[i];
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Canonicalize one exact ABAP data-source identity, or throw.
|
|
67
|
+
*
|
|
68
|
+
* Order matters and is part of the contract: ASCII-trim, then validate the RAW value, then fold
|
|
69
|
+
* case. Nothing is ever silently removed — a name that would need character stripping is rejected.
|
|
70
|
+
*
|
|
71
|
+
* @param raw the value as supplied by configuration, a caller, or SAP metadata
|
|
72
|
+
* @param what short label used in the error message (for example `SAP_BLOCKED_DATA_SOURCES entry #2`)
|
|
73
|
+
*/
|
|
74
|
+
export function canonicalDataSourceName(raw, what = 'data-source name') {
|
|
75
|
+
const trimmed = asciiTrim(raw);
|
|
76
|
+
if (trimmed.length === 0) {
|
|
77
|
+
throw new DataSourceNameError(`${what} is empty`);
|
|
78
|
+
}
|
|
79
|
+
if (trimmed.length > MAX_DATA_SOURCE_NAME_LENGTH) {
|
|
80
|
+
throw new DataSourceNameError(`${what} exceeds the ${MAX_DATA_SOURCE_NAME_LENGTH}-character limit (${trimmed.length} characters)`);
|
|
81
|
+
}
|
|
82
|
+
if (!ASCII_ALLOWED.test(trimmed)) {
|
|
83
|
+
throw new DataSourceNameError(`${what} ${echoToken(trimmed)} is not an exact technical name: only ASCII A-Z, a-z, 0-9, _, / and $ are allowed ` +
|
|
84
|
+
'(no wildcards, type prefixes, negation, quoting, regex syntax, or non-ASCII characters)');
|
|
85
|
+
}
|
|
86
|
+
if (!HAS_ALPHANUMERIC.test(trimmed)) {
|
|
87
|
+
throw new DataSourceNameError(`${what} ${echoToken(trimmed)} needs at least one ASCII letter or digit`);
|
|
88
|
+
}
|
|
89
|
+
return upperCaseAscii(trimmed);
|
|
90
|
+
}
|
|
91
|
+
/** Canonicalize a list, dropping exact duplicates while keeping first-occurrence order. */
|
|
92
|
+
export function canonicalDataSourceNames(raws, what = 'data-source name') {
|
|
93
|
+
const seen = new Set();
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const raw of raws) {
|
|
96
|
+
const name = canonicalDataSourceName(raw, what);
|
|
97
|
+
if (!seen.has(name)) {
|
|
98
|
+
seen.add(name);
|
|
99
|
+
out.push(name);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse the `SAP_BLOCKED_DATA_SOURCES` CSV contract.
|
|
106
|
+
*
|
|
107
|
+
* - unset (caller passes `undefined`), empty, or ASCII-whitespace-only → off (empty list);
|
|
108
|
+
* - once the ASCII-trimmed value is non-empty, EVERY comma-separated field is mandatory, so
|
|
109
|
+
* `,` `,,,` `,USR02` `USR02,` and `USR02,,PA0002` are startup errors rather than silently dropped;
|
|
110
|
+
* - each field is canonicalized with {@link canonicalDataSourceName};
|
|
111
|
+
* - duplicates collapse while preserving first-occurrence order.
|
|
112
|
+
*
|
|
113
|
+
* A value consisting only of NON-ASCII whitespace (for example U+3000) is deliberately an error, not
|
|
114
|
+
* "off": it is indistinguishable from a corrupted deployment value, and only ASCII blanks are the
|
|
115
|
+
* documented off switch.
|
|
116
|
+
*/
|
|
117
|
+
export function parseBlockedDataSourcesCsv(raw, sourceLabel = 'SAP_BLOCKED_DATA_SOURCES') {
|
|
118
|
+
if (asciiTrim(raw).length === 0)
|
|
119
|
+
return [];
|
|
120
|
+
const fields = raw.split(',');
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
const out = [];
|
|
123
|
+
for (let i = 0; i < fields.length; i++) {
|
|
124
|
+
const position = `${sourceLabel} entry #${i + 1} of ${fields.length}`;
|
|
125
|
+
if (asciiTrim(fields[i]).length === 0) {
|
|
126
|
+
throw new DataSourceNameError(`${position} is empty: leading, trailing, repeated, and separator-only commas are not allowed. ` +
|
|
127
|
+
`Set ${sourceLabel} to an empty value to turn the policy off.`);
|
|
128
|
+
}
|
|
129
|
+
const name = canonicalDataSourceName(fields[i], position);
|
|
130
|
+
if (!seen.has(name)) {
|
|
131
|
+
seen.add(name);
|
|
132
|
+
out.push(name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Deterministic policy fingerprint: SHA-256 over `data-source-blocklist:v1\n` followed by the
|
|
139
|
+
* sorted, unique, canonical names joined with `\n`.
|
|
140
|
+
*
|
|
141
|
+
* Order- and duplicate-independent, so two deployments with the same effective policy produce the
|
|
142
|
+
* same value. This is a configuration-drift and correlation identifier ONLY — it is unsalted by
|
|
143
|
+
* design, the candidate name space is small and guessable, and it must never be described as
|
|
144
|
+
* protecting the contents of the list. Exact values live on administrator-only surfaces.
|
|
145
|
+
*/
|
|
146
|
+
export function dataSourcePolicyFingerprint(names) {
|
|
147
|
+
const canonical = [...new Set(names)].sort();
|
|
148
|
+
return createHash('sha256')
|
|
149
|
+
.update(`data-source-blocklist:v1\n${canonical.join('\n')}`)
|
|
150
|
+
.digest('hex');
|
|
151
|
+
}
|
|
152
|
+
/** Short human-facing form of {@link dataSourcePolicyFingerprint} for startup logs. */
|
|
153
|
+
export function shortPolicyFingerprint(names) {
|
|
154
|
+
return dataSourcePolicyFingerprint(names).slice(0, 12);
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=data-source-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source-name.js","sourceRoot":"","sources":["../../src/adt/data-source-name.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/** Experimental exact-name data-source blocklist and live CDS-lineage evaluation. */
|
|
2
|
+
import { AdtSafetyError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Stable, distinct client-facing outcomes.
|
|
5
|
+
*
|
|
6
|
+
* - `DATA_SOURCE_BLOCKED` an exact configured rule matched.
|
|
7
|
+
* - `DATA_LINEAGE_UNRESOLVED` SAP metadata/identity/graph/replacement lineage could not be proven.
|
|
8
|
+
* - `DATA_SQL_UNSUPPORTED` the caller's SQL is outside the strict accepted grammar.
|
|
9
|
+
*
|
|
10
|
+
* They stay distinguishable on purpose: a model that cannot tell "blocked by policy" from "SQL not
|
|
11
|
+
* supported" cannot self-correct. That does permit coarse membership probing, which is a documented,
|
|
12
|
+
* deliberate trade rather than an oversight.
|
|
13
|
+
*/
|
|
14
|
+
export type DataSourcePolicyErrorCode = 'DATA_SOURCE_BLOCKED' | 'DATA_LINEAGE_UNRESOLVED' | 'DATA_SQL_UNSUPPORTED';
|
|
15
|
+
/** Opaque, bounded, non-secret correlation id shared by the client error and the audit record. */
|
|
16
|
+
export declare function newDecisionId(): string;
|
|
17
|
+
export declare class DataSourcePolicyError extends AdtSafetyError {
|
|
18
|
+
readonly code: DataSourcePolicyErrorCode;
|
|
19
|
+
readonly directSource: string;
|
|
20
|
+
readonly sourcePath: string[];
|
|
21
|
+
readonly decisionId: string;
|
|
22
|
+
readonly matchedSource?: string;
|
|
23
|
+
readonly reason: string;
|
|
24
|
+
/** Always false: every policy denial happens before the SAP data request is submitted. */
|
|
25
|
+
readonly executed = false;
|
|
26
|
+
constructor(code: DataSourcePolicyErrorCode, directSource: string, sourcePath: string[], reason: string, options?: {
|
|
27
|
+
decisionId?: string;
|
|
28
|
+
matchedSource?: string;
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Client-facing text.
|
|
32
|
+
*
|
|
33
|
+
* `ARC1_MINIMAL_ERRORS` is a CLIENT DISCLOSURE control only: it removes the direct root, the
|
|
34
|
+
* matched rule, the dependency path and the configuration variable name, keeping just enough for
|
|
35
|
+
* the model to act (stable code, executed=false, decision id, and a safe alternative). It does not
|
|
36
|
+
* change the decision and does not reduce what the audit event records.
|
|
37
|
+
*
|
|
38
|
+
* The three codes stay distinguishable even in minimal mode, which does permit coarse membership
|
|
39
|
+
* probing. That is a deliberate, documented trade: a model that cannot tell "blocked by policy"
|
|
40
|
+
* from "SQL not supported" cannot correct itself.
|
|
41
|
+
*/
|
|
42
|
+
clientMessage(minimalErrors: boolean): string;
|
|
43
|
+
/** Guidance that does not reveal policy-sensitive names. */
|
|
44
|
+
private safeAlternative;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* SQL node kinds SAP emits in the dependency branch. Verified live on SAP_BASIS 750, 758 and 816.
|
|
48
|
+
* Anything outside this set inside a SQL branch fails closed.
|
|
49
|
+
*/
|
|
50
|
+
export declare const SQL_NODE_KINDS: readonly ['CDS_VIEW', 'TABLE', 'CDS_TABLE_FUNCTION'];
|
|
51
|
+
export type SqlNodeKind = (typeof SQL_NODE_KINDS)[number];
|
|
52
|
+
export interface CdsDependencyNode {
|
|
53
|
+
name: string;
|
|
54
|
+
aliases: string[];
|
|
55
|
+
kind: SqlNodeKind;
|
|
56
|
+
relation?: string;
|
|
57
|
+
databaseExists?: boolean;
|
|
58
|
+
/** Access-control presence, retained as audit context only — never an authorization decision. */
|
|
59
|
+
accessControlled: boolean;
|
|
60
|
+
children: CdsDependencyNode[];
|
|
61
|
+
}
|
|
62
|
+
export type ResolvedDirectDataSource = {
|
|
63
|
+
kind: 'table';
|
|
64
|
+
name: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'cds';
|
|
67
|
+
name: string;
|
|
68
|
+
ddlSource: string;
|
|
69
|
+
} | {
|
|
70
|
+
kind: 'classic-view' | 'structure' | 'unknown';
|
|
71
|
+
name: string;
|
|
72
|
+
};
|
|
73
|
+
export interface DataSourcePolicyResolver {
|
|
74
|
+
resolveDirectSource(name: string): Promise<ResolvedDirectDataSource>;
|
|
75
|
+
readTableSource(name: string): Promise<string>;
|
|
76
|
+
readCdsDependencyGraph(ddlSource: string): Promise<CdsDependencyNode>;
|
|
77
|
+
}
|
|
78
|
+
export interface DataSourcePolicyBackend {
|
|
79
|
+
searchObject(name: string, maxResults: number): Promise<Array<{
|
|
80
|
+
objectName: string;
|
|
81
|
+
objectType: string;
|
|
82
|
+
uri: string;
|
|
83
|
+
}>>;
|
|
84
|
+
readTableSource(name: string): Promise<string>;
|
|
85
|
+
dependencyGraphAccept(): string | undefined;
|
|
86
|
+
readDependencyGraph(path: string, accept: string): Promise<string>;
|
|
87
|
+
}
|
|
88
|
+
/** Request-scoped adapter from ADT metadata reads to the pure lineage evaluator. */
|
|
89
|
+
export declare class DataSourceBlocklistGuard {
|
|
90
|
+
private readonly blockedSources;
|
|
91
|
+
private readonly backend;
|
|
92
|
+
/** Per-decision instrumentation. The guard is constructed per logical request and never reused. */
|
|
93
|
+
private metadataRequests;
|
|
94
|
+
private graphNodes;
|
|
95
|
+
constructor(blockedSources: string[], backend: DataSourcePolicyBackend);
|
|
96
|
+
/**
|
|
97
|
+
* Run one policy decision and record exactly one audit event for it, allow or deny.
|
|
98
|
+
*
|
|
99
|
+
* The audit record is the protected copy: it always carries the complete normalized decision, so
|
|
100
|
+
* an operator can reconstruct a denial even when the client was told almost nothing.
|
|
101
|
+
*/
|
|
102
|
+
private decide;
|
|
103
|
+
enforceTableContents(tableName: string, sqlFilter?: string): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Authorize every statement of one logical request together.
|
|
106
|
+
*
|
|
107
|
+
* The union of canonical direct sources is deduplicated before any SAP call, so N chunks that all
|
|
108
|
+
* read the same table cost exactly one lineage resolution, and a source appearing in only one chunk
|
|
109
|
+
* still denies the whole batch.
|
|
110
|
+
*/
|
|
111
|
+
enforceSqlBatch(statements: string[]): Promise<void>;
|
|
112
|
+
private analyzeOrThrow;
|
|
113
|
+
enforceSources(directSources: string[]): Promise<void>;
|
|
114
|
+
/** The evaluation itself, without audit framing, so exactly one event is emitted per request. */
|
|
115
|
+
private evaluate;
|
|
116
|
+
private resolveDirectSource;
|
|
117
|
+
private readCdsDependencyGraph;
|
|
118
|
+
}
|
|
119
|
+
/** Endpoint the SQL dependency graph is served from. */
|
|
120
|
+
export declare const CDS_DEPENDENCY_GRAPH_PATH = "/sap/bc/adt/ddic/ddl/dependencies/graphdata";
|
|
121
|
+
/**
|
|
122
|
+
* Wire a request-scoped guard from the ADT primitives it needs.
|
|
123
|
+
*
|
|
124
|
+
* Lives here rather than on the client so the ADT facade stays a facade: this is policy wiring, and
|
|
125
|
+
* a new guard is built per logical request so its instrumentation can never leak between decisions.
|
|
126
|
+
*/
|
|
127
|
+
export declare function createDataSourceBlocklistGuard(deps: {
|
|
128
|
+
blockedDataSources: string[];
|
|
129
|
+
searchObject: DataSourcePolicyBackend['searchObject'];
|
|
130
|
+
readTableSource: DataSourcePolicyBackend['readTableSource'];
|
|
131
|
+
dependencyGraphAccept: DataSourcePolicyBackend['dependencyGraphAccept'];
|
|
132
|
+
readDependencyGraph: DataSourcePolicyBackend['readDependencyGraph'];
|
|
133
|
+
}): DataSourceBlocklistGuard;
|
|
134
|
+
/**
|
|
135
|
+
* Parse SAP dependency-analyzer XML (750 element-info and 758/816 SQLDependencyModel v3) into one
|
|
136
|
+
* bounded, explicitly classified tree.
|
|
137
|
+
*
|
|
138
|
+
* Every node is classified before it can influence a decision. SQL nodes enter lineage; the verified
|
|
139
|
+
* auxiliary access-control chain is validated and dropped; anything else fails closed. Explicit kind
|
|
140
|
+
* classification is the primary boundary here — a later failed table-source read is defence in depth,
|
|
141
|
+
* not the protection.
|
|
142
|
+
*/
|
|
143
|
+
export declare function parseCdsDependencyGraph(xml: string): CdsDependencyNode;
|
|
144
|
+
/** Return a table's active CDS replacement object; malformed or duplicate annotations fail closed. */
|
|
145
|
+
export declare function extractReplacementObject(source: string): string | undefined;
|
|
146
|
+
/** Evaluate direct sources and their active lineage before a data-preview request is sent. */
|
|
147
|
+
export declare function enforceBlockedDataSources(directSources: string[], configuredBlockedSources: string[], resolver: DataSourcePolicyResolver): Promise<void>;
|
|
148
|
+
//# sourceMappingURL=data-source-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-source-policy.d.ts","sourceRoot":"","sources":["../../src/adt/data-source-policy.ts"],"names":[],"mappings":"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"}
|