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,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conservative ABAP SQL source analysis for the experimental data-source blocklist.
|
|
3
|
+
*
|
|
4
|
+
* This is an authorization parser, not a formatter or best-effort dependency extractor:
|
|
5
|
+
* anything that cannot be proven to be one complete static SELECT/WITH statement is refused.
|
|
6
|
+
*/
|
|
7
|
+
import { Expressions, MemoryFile, Registry, Version } from '@abaplint/core';
|
|
8
|
+
import { getDefaultAbaplintConfig } from '../lint/abaplint-config-cache.js';
|
|
9
|
+
import { canonicalDataSourceName } from './data-source-name.js';
|
|
10
|
+
const MAX_SQL_LENGTH = 100_000;
|
|
11
|
+
const SYNTHETIC_TARGET = '__arc1_policy_result';
|
|
12
|
+
const FORBIDDEN_EXPRESSIONS = [
|
|
13
|
+
['privileged access', Expressions.SQLPrivilegedAccess],
|
|
14
|
+
['client override', Expressions.SQLClient],
|
|
15
|
+
['secondary database connection', Expressions.DatabaseConnection],
|
|
16
|
+
['dynamic expression', Expressions.Dynamic],
|
|
17
|
+
['CDS association path', Expressions.SQLPathForEntity],
|
|
18
|
+
['CDS column path', Expressions.SQLPathForColumn],
|
|
19
|
+
['external PROVIDED BY source', Expressions.SQLProvidedBy],
|
|
20
|
+
];
|
|
21
|
+
export class SqlSourceAnalysisError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(`SQL source analysis failed: ${message}`);
|
|
24
|
+
this.name = 'SqlSourceAnalysisError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Canonicalize a source the parser found, reporting failure as a SQL-grammar problem.
|
|
29
|
+
*
|
|
30
|
+
* Identity rules live in one place (`data-source-name.ts`); a name the shared canonicalizer refuses
|
|
31
|
+
* means the statement is outside the accepted subset, not that lineage is unresolved.
|
|
32
|
+
*/
|
|
33
|
+
function parsedSourceName(raw) {
|
|
34
|
+
try {
|
|
35
|
+
return canonicalDataSourceName(raw, 'SQL data source');
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new SqlSourceAnalysisError(error instanceof Error ? error.message : String(error));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Refuse lexical constructs the strict subset does not accept, before the statement is parsed.
|
|
43
|
+
*
|
|
44
|
+
* `@abaplint/core` strips ABAP comments during lexing, so a commented region is invisible to the
|
|
45
|
+
* security parser while the ORIGINAL, unmodified string — comment included — is what is posted to
|
|
46
|
+
* `/sap/bc/adt/datapreview/freestyle`. That divergence is not a demonstrated exploit: live SAP_BASIS
|
|
47
|
+
* 758 answered an inline quote comment with HTTP 400 and ignored a column-one asterisk comment,
|
|
48
|
+
* matching ordinary ABAP comment semantics. It is refused anyway for a narrower reason — while
|
|
49
|
+
* comments are accepted, the text the analyzer inspects is not the text SAP receives, and that is
|
|
50
|
+
* only safe because of an assumption about a parser ARC-1 does not control. Rejecting them removes
|
|
51
|
+
* the assumption and shrinks the accepted grammar at no cost.
|
|
52
|
+
*
|
|
53
|
+
* Lexing is literal-aware: a double quote or semicolon INSIDE a single-quoted literal is legitimate
|
|
54
|
+
* (`WHERE X = 'a"b'`) and stays accepted, and doubled single quotes (`'it''s'`) are escaped quotes,
|
|
55
|
+
* not literal terminators.
|
|
56
|
+
*/
|
|
57
|
+
function rejectUnsupportedLexicalConstructs(sql) {
|
|
58
|
+
let inLiteral = false;
|
|
59
|
+
let column = 0;
|
|
60
|
+
for (let i = 0; i < sql.length; i++) {
|
|
61
|
+
const char = sql[i];
|
|
62
|
+
if (char === '\n' || char === '\r') {
|
|
63
|
+
column = 0;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const atColumnOne = column === 0;
|
|
67
|
+
column += 1;
|
|
68
|
+
if (inLiteral) {
|
|
69
|
+
if (char === "'") {
|
|
70
|
+
if (sql[i + 1] === "'") {
|
|
71
|
+
i += 1;
|
|
72
|
+
column += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
inLiteral = false;
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (char === "'") {
|
|
80
|
+
inLiteral = true;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (char === '"') {
|
|
84
|
+
throw new SqlSourceAnalysisError('ABAP inline comments (") are not accepted by the strict SQL subset; remove the comment and resubmit');
|
|
85
|
+
}
|
|
86
|
+
if (char === ';') {
|
|
87
|
+
throw new SqlSourceAnalysisError('semicolons are not accepted by the strict SQL subset');
|
|
88
|
+
}
|
|
89
|
+
if (atColumnOne && char === '*') {
|
|
90
|
+
throw new SqlSourceAnalysisError('ABAP full-line comments (* in column one) are not accepted by the strict SQL subset; remove the comment and resubmit');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (inLiteral)
|
|
94
|
+
throw new SqlSourceAnalysisError('unterminated string literal');
|
|
95
|
+
}
|
|
96
|
+
/** Extract every static database source from one complete ABAP SQL SELECT/WITH statement. */
|
|
97
|
+
export function analyzeSqlDataSources(sql) {
|
|
98
|
+
if (sql.length > MAX_SQL_LENGTH) {
|
|
99
|
+
throw new SqlSourceAnalysisError(`SQL is too long for policy analysis (maximum ${MAX_SQL_LENGTH} characters)`);
|
|
100
|
+
}
|
|
101
|
+
const trimmed = sql.trim();
|
|
102
|
+
if (!trimmed)
|
|
103
|
+
throw new SqlSourceAnalysisError('SQL is empty');
|
|
104
|
+
// Runs on the trimmed text, which is exactly what is embedded below, so column-one detection
|
|
105
|
+
// matches what the parser sees.
|
|
106
|
+
rejectUnsupportedLexicalConstructs(trimmed);
|
|
107
|
+
const inputLineCount = trimmed.split(/\r?\n/).length;
|
|
108
|
+
const syntheticTargetRow = inputLineCount + 3;
|
|
109
|
+
const source = `REPORT zarc1_policy.\nSTART-OF-SELECTION.\n${trimmed}\n` + `INTO TABLE @DATA(${SYNTHETIC_TARGET}).`;
|
|
110
|
+
const registry = new Registry(getDefaultAbaplintConfig(Version.v758));
|
|
111
|
+
registry.addFile(new MemoryFile('zarc1_policy.prog.abap', source));
|
|
112
|
+
registry.parse();
|
|
113
|
+
const parserIssues = registry.findIssues().filter((issue) => issue.getKey() === 'parser_error');
|
|
114
|
+
if (parserIssues.length > 0) {
|
|
115
|
+
throw new SqlSourceAnalysisError('statement is malformed or unsupported by the security parser');
|
|
116
|
+
}
|
|
117
|
+
const objects = Array.from(registry.getObjects());
|
|
118
|
+
const object = objects[0];
|
|
119
|
+
const structure = object?.getMainABAPFile?.()?.getStructure?.();
|
|
120
|
+
if (objects.length !== 1 || !structure) {
|
|
121
|
+
throw new SqlSourceAnalysisError('statement did not produce one complete ABAP program syntax tree');
|
|
122
|
+
}
|
|
123
|
+
const statements = structure.findAllStatementNodes();
|
|
124
|
+
if (statements.length !== 3 ||
|
|
125
|
+
statements[0]?.get().constructor.name !== 'Report' ||
|
|
126
|
+
statements[1]?.get().constructor.name !== 'StartOfSelection' ||
|
|
127
|
+
!['Select', 'With'].includes(statements[2]?.get().constructor.name ?? '')) {
|
|
128
|
+
throw new SqlSourceAnalysisError('exactly one SELECT or WITH statement is required');
|
|
129
|
+
}
|
|
130
|
+
const queryStatement = statements[2];
|
|
131
|
+
const tokens = queryStatement.getTokens();
|
|
132
|
+
const syntheticTokens = tokens
|
|
133
|
+
.filter((token) => token.getRow() === syntheticTargetRow)
|
|
134
|
+
.map((token) => token.getStr());
|
|
135
|
+
const expectedTarget = ['INTO', 'TABLE', '@', 'DATA', '(', SYNTHETIC_TARGET, ')', '.'];
|
|
136
|
+
if (syntheticTokens.length !== expectedTarget.length ||
|
|
137
|
+
syntheticTokens.some((token, i) => token !== expectedTarget[i])) {
|
|
138
|
+
throw new SqlSourceAnalysisError('statement is incomplete or already contains an output target');
|
|
139
|
+
}
|
|
140
|
+
const callerHostMarkers = tokens.filter((token) => token.constructor.name === 'WAt' && token.getRow() !== syntheticTargetRow);
|
|
141
|
+
if (callerHostMarkers.length > 0) {
|
|
142
|
+
throw new SqlSourceAnalysisError('host expressions are not allowed while the data-source blocklist is active');
|
|
143
|
+
}
|
|
144
|
+
for (const [label, expression] of FORBIDDEN_EXPRESSIONS) {
|
|
145
|
+
if (structure.findAllExpressionsRecursive(expression).length > 0) {
|
|
146
|
+
throw new SqlSourceAnalysisError(`${label} is not allowed while the data-source blocklist is active`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const sources = structure
|
|
150
|
+
.findAllExpressionsRecursive(Expressions.DatabaseTable)
|
|
151
|
+
.map((node) => parsedSourceName(node.concatTokens()));
|
|
152
|
+
const uniqueSources = [...new Set(sources)];
|
|
153
|
+
if (uniqueSources.length === 0) {
|
|
154
|
+
throw new SqlSourceAnalysisError('no static database source could be proven');
|
|
155
|
+
}
|
|
156
|
+
return uniqueSources;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=sql-source-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-source-analyzer.js","sourceRoot":"","sources":["../../src/adt/sql-source-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Safe SQL builder and row-limit helpers for ADT data-preview requests. */
|
|
2
|
+
/** Coerce a caller-supplied row limit into [1, MAX_TABLE_QUERY_ROWS]. */
|
|
3
|
+
export declare function clampPreviewRows(requested: number | undefined, fallback?: number): number;
|
|
4
|
+
/**
|
|
5
|
+
* Validate a caller-supplied SQL identifier exactly. It is NEVER rewritten.
|
|
6
|
+
*
|
|
7
|
+
* The previous implementation stripped disallowed characters (`raw.toUpperCase().replace(/[^\w/]/g, '')`).
|
|
8
|
+
* That made the identifier ARC-1 authorized differ from the identifier it executed: `USR02$` was checked
|
|
9
|
+
* as `USR02$` but executed as `USR02`. Silent rewriting is therefore banned outright — including when
|
|
10
|
+
* the blocklist is off, so the identifier contract does not depend on policy state. The only permitted
|
|
11
|
+
* transformation is ASCII case folding, which is what makes "checked name" and "executed name"
|
|
12
|
+
* byte-for-byte equal.
|
|
13
|
+
*/
|
|
14
|
+
export declare function exactSqlIdentifier(raw: string, kind: 'table' | 'column' | 'field'): string;
|
|
15
|
+
/**
|
|
16
|
+
* Build a safe static SELECT from structured TABLE_QUERY parameters.
|
|
17
|
+
*
|
|
18
|
+
* Identifiers are validated, not sanitized; values are quoted and escaped, and IN/NOT IN values
|
|
19
|
+
* cannot become subqueries. Pass an already-canonical table name when the caller has authorized it,
|
|
20
|
+
* so the authorized and executed identities are the same string.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildTableQuerySql(tableName: string, columns?: string[], where?: Array<{
|
|
23
|
+
field: string;
|
|
24
|
+
op: string;
|
|
25
|
+
value?: string;
|
|
26
|
+
}>): string;
|
|
27
|
+
/**
|
|
28
|
+
* Execute statements that have ALREADY been authorized by the data-source policy.
|
|
29
|
+
*
|
|
30
|
+
* Kept beside the SQL builder rather than in the ADT facade, but deliberately NOT exported as a
|
|
31
|
+
* public client method: authorization and execution must stay inside one private client operation so
|
|
32
|
+
* no caller can obtain a reusable "already authorized" handle.
|
|
33
|
+
*/
|
|
34
|
+
export declare function executeDataPreviewStatements(post: (sql: string, rowLimit: number) => Promise<string>, parse: (body: string) => {
|
|
35
|
+
columns: string[];
|
|
36
|
+
rows: Record<string, string>[];
|
|
37
|
+
}, statements: string[], rowLimit: number): Promise<{
|
|
38
|
+
columns: string[];
|
|
39
|
+
rows: Record<string, string>[];
|
|
40
|
+
}>;
|
|
41
|
+
//# sourceMappingURL=table-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-query.d.ts","sourceRoot":"","sources":["../../src/adt/table-query.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAoC5E,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,SAAM,GAAG,MAAM,CAGtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAE1F;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAAE,EAClB,KAAK,CAAC,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC3D,MAAM,CAmBR;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EACxD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAAE,EAC9E,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAAE,CAAC,CAWhE"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** Safe SQL builder and row-limit helpers for ADT data-preview requests. */
|
|
2
|
+
import { canonicalDataSourceName } from './data-source-name.js';
|
|
3
|
+
/** Allowed SQL comparison operators for TABLE_QUERY where conditions. */
|
|
4
|
+
const ALLOWED_OPS = new Set([
|
|
5
|
+
'=',
|
|
6
|
+
'!=',
|
|
7
|
+
'<>',
|
|
8
|
+
'<',
|
|
9
|
+
'<=',
|
|
10
|
+
'>',
|
|
11
|
+
'>=',
|
|
12
|
+
'LIKE',
|
|
13
|
+
'NOT LIKE',
|
|
14
|
+
'IN',
|
|
15
|
+
'NOT IN',
|
|
16
|
+
'IS NULL',
|
|
17
|
+
'IS NOT NULL',
|
|
18
|
+
]);
|
|
19
|
+
// BETWEEN is intentionally excluded: the value would require parsing "low AND high"
|
|
20
|
+
// where AND is a reserved word, making safe escaping complex and error-prone.
|
|
21
|
+
// Use two separate conditions (>= low, <= high) instead.
|
|
22
|
+
/** Build a safe IN/NOT IN list from comma-separated raw values. */
|
|
23
|
+
function buildInList(raw) {
|
|
24
|
+
const trimmed = raw.trim();
|
|
25
|
+
const inner = trimmed.startsWith('(') && trimmed.endsWith(')') ? trimmed.slice(1, -1) : trimmed;
|
|
26
|
+
const parts = inner.split(',').map((part) => `'${part.trim().replace(/'/g, "''")}'`);
|
|
27
|
+
return `(${parts.join(', ')})`;
|
|
28
|
+
}
|
|
29
|
+
/** Upper bound for an in-memory TABLE_QUERY result. */
|
|
30
|
+
const MAX_TABLE_QUERY_ROWS = 10_000;
|
|
31
|
+
/** Coerce a caller-supplied row limit into [1, MAX_TABLE_QUERY_ROWS]. */
|
|
32
|
+
export function clampPreviewRows(requested, fallback = 100) {
|
|
33
|
+
if (requested === undefined || !Number.isFinite(requested) || requested < 1)
|
|
34
|
+
return fallback;
|
|
35
|
+
return Math.min(Math.floor(requested), MAX_TABLE_QUERY_ROWS);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate a caller-supplied SQL identifier exactly. It is NEVER rewritten.
|
|
39
|
+
*
|
|
40
|
+
* The previous implementation stripped disallowed characters (`raw.toUpperCase().replace(/[^\w/]/g, '')`).
|
|
41
|
+
* That made the identifier ARC-1 authorized differ from the identifier it executed: `USR02$` was checked
|
|
42
|
+
* as `USR02$` but executed as `USR02`. Silent rewriting is therefore banned outright — including when
|
|
43
|
+
* the blocklist is off, so the identifier contract does not depend on policy state. The only permitted
|
|
44
|
+
* transformation is ASCII case folding, which is what makes "checked name" and "executed name"
|
|
45
|
+
* byte-for-byte equal.
|
|
46
|
+
*/
|
|
47
|
+
export function exactSqlIdentifier(raw, kind) {
|
|
48
|
+
return canonicalDataSourceName(raw, `TABLE_QUERY ${kind} name`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build a safe static SELECT from structured TABLE_QUERY parameters.
|
|
52
|
+
*
|
|
53
|
+
* Identifiers are validated, not sanitized; values are quoted and escaped, and IN/NOT IN values
|
|
54
|
+
* cannot become subqueries. Pass an already-canonical table name when the caller has authorized it,
|
|
55
|
+
* so the authorized and executed identities are the same string.
|
|
56
|
+
*/
|
|
57
|
+
export function buildTableQuerySql(tableName, columns, where) {
|
|
58
|
+
const safeTable = exactSqlIdentifier(tableName, 'table');
|
|
59
|
+
const colList = columns?.length ? columns.map((column) => exactSqlIdentifier(column, 'column')).join(', ') : '*';
|
|
60
|
+
let sql = `SELECT ${colList} FROM ${safeTable}`;
|
|
61
|
+
if (where?.length) {
|
|
62
|
+
const clauses = where.map(({ field, op, value }) => {
|
|
63
|
+
const safeField = exactSqlIdentifier(field, 'field');
|
|
64
|
+
const safeOp = op.trim().toUpperCase();
|
|
65
|
+
if (!ALLOWED_OPS.has(safeOp))
|
|
66
|
+
throw new Error(`TABLE_QUERY: operator "${op}" is not allowed`);
|
|
67
|
+
if (safeOp === 'IS NULL' || safeOp === 'IS NOT NULL')
|
|
68
|
+
return `${safeField} ${safeOp}`;
|
|
69
|
+
if (safeOp === 'IN' || safeOp === 'NOT IN')
|
|
70
|
+
return `${safeField} ${safeOp} ${buildInList(String(value ?? ''))}`;
|
|
71
|
+
return `${safeField} ${safeOp} '${String(value ?? '').replace(/'/g, "''")}'`;
|
|
72
|
+
});
|
|
73
|
+
sql += ` WHERE ${clauses.join(' AND ')}`;
|
|
74
|
+
}
|
|
75
|
+
// ADT freestyle SQL rejects ORDER BY on NW 7.50/7.51; callers sort client-side.
|
|
76
|
+
return sql;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Execute statements that have ALREADY been authorized by the data-source policy.
|
|
80
|
+
*
|
|
81
|
+
* Kept beside the SQL builder rather than in the ADT facade, but deliberately NOT exported as a
|
|
82
|
+
* public client method: authorization and execution must stay inside one private client operation so
|
|
83
|
+
* no caller can obtain a reusable "already authorized" handle.
|
|
84
|
+
*/
|
|
85
|
+
export async function executeDataPreviewStatements(post, parse, statements, rowLimit) {
|
|
86
|
+
const rows = [];
|
|
87
|
+
let columns = [];
|
|
88
|
+
for (const statement of statements) {
|
|
89
|
+
const remaining = rowLimit - rows.length;
|
|
90
|
+
if (remaining <= 0)
|
|
91
|
+
break;
|
|
92
|
+
const chunk = parse(await post(statement, remaining));
|
|
93
|
+
if (columns.length === 0)
|
|
94
|
+
columns = chunk.columns;
|
|
95
|
+
rows.push(...chunk.rows);
|
|
96
|
+
}
|
|
97
|
+
return { columns, rows: rows.slice(0, rowLimit) };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=table-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-query.js","sourceRoot":"","sources":["../../src/adt/table-query.ts"],"names":[],"mappings":"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"}
|
package/dist/adt/xml-parser.d.ts
CHANGED
|
@@ -79,21 +79,6 @@ export declare function parsePackageContents(xml: string): Array<{
|
|
|
79
79
|
* the resolver relies on this to fail closed.
|
|
80
80
|
*/
|
|
81
81
|
export declare function parseSubpackageNodestructure(xml: string): string[];
|
|
82
|
-
/**
|
|
83
|
-
* Parse table contents (datapreview response).
|
|
84
|
-
*
|
|
85
|
-
* SAP ADT returns two possible formats for data preview:
|
|
86
|
-
*
|
|
87
|
-
* Format 1 (older/asx): COLUMNS/COLUMN/METADATA + DATASET/DATA
|
|
88
|
-
* Format 2 (newer/dataPreview namespace): columns/metadata + dataSet/data
|
|
89
|
-
*
|
|
90
|
-
* After namespace stripping, both converge but with different casing.
|
|
91
|
-
* We try both patterns with fallback.
|
|
92
|
-
*/
|
|
93
|
-
export declare function parseTableContents(xml: string): {
|
|
94
|
-
columns: string[];
|
|
95
|
-
rows: Record<string, string>[];
|
|
96
|
-
};
|
|
97
82
|
/** Metrics ADT returns alongside a datapreview result (already in the body, normally discarded). */
|
|
98
83
|
export interface DataPreviewMeta {
|
|
99
84
|
/** True number of rows the query matched, independent of the rowNumber cap. */
|
|
@@ -103,6 +88,16 @@ export interface DataPreviewMeta {
|
|
|
103
88
|
/** The actual OpenSQL ADT generated and ran (whitespace-collapsed). */
|
|
104
89
|
executedQueryString?: string;
|
|
105
90
|
}
|
|
91
|
+
export interface DataPreviewResult extends DataPreviewMeta {
|
|
92
|
+
columns: string[];
|
|
93
|
+
rows: Record<string, string>[];
|
|
94
|
+
}
|
|
95
|
+
/** Parse data-preview rows and metrics from one shared fast-xml-parser object tree. */
|
|
96
|
+
export declare function parseDataPreviewResult(xml: string): DataPreviewResult;
|
|
97
|
+
export declare function parseTableContents(xml: string): {
|
|
98
|
+
columns: string[];
|
|
99
|
+
rows: Record<string, string>[];
|
|
100
|
+
};
|
|
106
101
|
/**
|
|
107
102
|
* Parse the scalar metrics from a datapreview response — `totalRows`, `queryExecutionTime`,
|
|
108
103
|
* `executedQueryString`. All optional: absent fields are omitted. Reads the same body
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xml-parser.d.ts","sourceRoot":"","sources":["../../src/adt/xml-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EACV,eAAe,EAEf,mBAAmB,EAEnB,sBAAsB,EACtB,UAAU,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EACf,UAAU,EACV,6BAA6B,EAC7B,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,cAAc,EAEd,gBAAgB,EAGhB,kBAAkB,EAClB,0BAA0B,EAC1B,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAoED,0CAA0C;AAC1C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE7D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAUjE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAczE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBlE;
|
|
1
|
+
{"version":3,"file":"xml-parser.d.ts","sourceRoot":"","sources":["../../src/adt/xml-parser.ts"],"names":[],"mappings":"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"}
|
package/dist/adt/xml-parser.js
CHANGED
|
@@ -204,8 +204,7 @@ export function parseSubpackageNodestructure(xml) {
|
|
|
204
204
|
* After namespace stripping, both converge but with different casing.
|
|
205
205
|
* We try both patterns with fallback.
|
|
206
206
|
*/
|
|
207
|
-
|
|
208
|
-
const parsed = parseXml(xml);
|
|
207
|
+
function parseTableContentsObject(parsed) {
|
|
209
208
|
// Try old format first: abap > values > COLUMNS > COLUMN
|
|
210
209
|
let columns = getDeepArray(parsed, ['abap', 'values', 'COLUMNS', 'COLUMN']);
|
|
211
210
|
if (columns.length === 0) {
|
|
@@ -243,15 +242,7 @@ export function parseTableContents(xml) {
|
|
|
243
242
|
}
|
|
244
243
|
return { columns: colNames, rows };
|
|
245
244
|
}
|
|
246
|
-
|
|
247
|
-
* Parse the scalar metrics from a datapreview response — `totalRows`, `queryExecutionTime`,
|
|
248
|
-
* `executedQueryString`. All optional: absent fields are omitted. Reads the same body
|
|
249
|
-
* `parseTableContents` does; surfaced so SAPQuery can report real row counts + server-side timing.
|
|
250
|
-
*/
|
|
251
|
-
export function parseDataPreviewMeta(xml) {
|
|
252
|
-
if (!xml || xml.trim().length === 0)
|
|
253
|
-
return {};
|
|
254
|
-
const parsed = parseXml(xml);
|
|
245
|
+
function parseDataPreviewMetaObject(parsed) {
|
|
255
246
|
const td = (parsed.tableData ?? parsed);
|
|
256
247
|
const meta = {};
|
|
257
248
|
// Guard blank values too: an empty <totalRows/> would otherwise coerce via Number('') to a spurious 0.
|
|
@@ -270,6 +261,28 @@ export function parseDataPreviewMeta(xml) {
|
|
|
270
261
|
}
|
|
271
262
|
return meta;
|
|
272
263
|
}
|
|
264
|
+
/** Parse data-preview rows and metrics from one shared fast-xml-parser object tree. */
|
|
265
|
+
export function parseDataPreviewResult(xml) {
|
|
266
|
+
if (!xml || xml.trim().length === 0)
|
|
267
|
+
return { columns: [], rows: [] };
|
|
268
|
+
const parsed = parseXml(xml);
|
|
269
|
+
return { ...parseTableContentsObject(parsed), ...parseDataPreviewMetaObject(parsed) };
|
|
270
|
+
}
|
|
271
|
+
export function parseTableContents(xml) {
|
|
272
|
+
if (!xml || xml.trim().length === 0)
|
|
273
|
+
return { columns: [], rows: [] };
|
|
274
|
+
return parseTableContentsObject(parseXml(xml));
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Parse the scalar metrics from a datapreview response — `totalRows`, `queryExecutionTime`,
|
|
278
|
+
* `executedQueryString`. All optional: absent fields are omitted. Reads the same body
|
|
279
|
+
* `parseTableContents` does; surfaced so SAPQuery can report real row counts + server-side timing.
|
|
280
|
+
*/
|
|
281
|
+
export function parseDataPreviewMeta(xml) {
|
|
282
|
+
if (!xml || xml.trim().length === 0)
|
|
283
|
+
return {};
|
|
284
|
+
return parseDataPreviewMetaObject(parseXml(xml));
|
|
285
|
+
}
|
|
273
286
|
/**
|
|
274
287
|
* Parse installed components response.
|
|
275
288
|
*
|