@saptools/cf-hana 0.4.1 → 0.5.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 +64 -0
- package/README.md +91 -1
- package/dist/cli.js +1060 -142
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +35 -4
- package/dist/index.js +1024 -120
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,70 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- cspell:words VARCHAR -->
|
|
4
4
|
|
|
5
|
+
## 0.5.1 - 2026-07-25
|
|
6
|
+
|
|
7
|
+
- **Security fix:** a `WITH`-prefixed write (e.g. `WITH x AS (...) DELETE
|
|
8
|
+
FROM ...`) and a bare `CALL <procedure>()` both previously slipped past
|
|
9
|
+
the `--read-only` and destructive-statement guards entirely — the former
|
|
10
|
+
was always classified as a harmless `SELECT` regardless of what it
|
|
11
|
+
actually did, and the latter was never classified as destructive at all,
|
|
12
|
+
so it ran with no `--allow-destructive` confirmation even in the default
|
|
13
|
+
(non-read-only) mode. Both are now correctly classified and guarded.
|
|
14
|
+
Anyone relying on `--read-only` or the destructive-statement guard as a
|
|
15
|
+
hard trust boundary should treat this as the release that closes that gap.
|
|
16
|
+
The automatic pre-write backup now also correctly recognizes and protects
|
|
17
|
+
a `WITH`-prefixed `UPDATE`/`DELETE`/`UPSERT`/`MERGE`, matching what a
|
|
18
|
+
non-`WITH` equivalent write already produced.
|
|
19
|
+
- Disable SAP HANA Cloud's reactive mid-auth redirect on every connection
|
|
20
|
+
(direct and tunneled): on a genuine multi-node HANA Cloud instance, the
|
|
21
|
+
server can redirect an already-established connection to an internal
|
|
22
|
+
per-node hostname that isn't reachable outside SAP's own network, which
|
|
23
|
+
previously defeated the SSH-tunnel fallback by silently abandoning the
|
|
24
|
+
tunnel for a fresh, untunneled connection that failed the same way the
|
|
25
|
+
original direct connection did.
|
|
26
|
+
- Fix the SSH-tunnel fallback's candidate discovery calling `cf apps`
|
|
27
|
+
(measured at up to ~20s against a large space) before ever trying the
|
|
28
|
+
already-known target app or a cached "last worked" app, which could
|
|
29
|
+
starve the entire shared fallback budget before a working candidate got a
|
|
30
|
+
chance. Known candidates are now always tried first; `cf apps` is only
|
|
31
|
+
called if none of them work, and is bounded to whatever budget remains
|
|
32
|
+
rather than retried on its own separate timeout/retry policy.
|
|
33
|
+
- Cap the direct-connect attempt's own timeout to the tunnel fallback's
|
|
34
|
+
per-candidate ceiling whenever a fallback is available, so a silently
|
|
35
|
+
hanging (rather than actively refused) direct connection can no longer
|
|
36
|
+
consume its full configured timeout before the tunnel path gets a chance
|
|
37
|
+
— lowering the documented worst-case latency from ~85s to ~40s.
|
|
38
|
+
- Fix `--refresh-tunnel` leaking the SSH process it superseded; the old
|
|
39
|
+
tunnel is now terminated instead of running until its keepalive elapses.
|
|
40
|
+
- Surface a failed tunnel candidate's actual stderr (e.g. "SSH disabled for
|
|
41
|
+
this app") via the existing connectivity status output on stderr, instead
|
|
42
|
+
of discarding it silently.
|
|
43
|
+
- Fix a freshly-established or reused tunnel being cached as usable even
|
|
44
|
+
when its own post-connect setup failed in a way that looked like a
|
|
45
|
+
dropped connection rather than a genuine, actionable HANA rejection.
|
|
46
|
+
- Bound how long a process waits for another concurrent invocation to
|
|
47
|
+
finish establishing the same host's tunnel, so the entire shared deadline
|
|
48
|
+
can no longer be spent waiting on a leader whose owning process has
|
|
49
|
+
already died.
|
|
50
|
+
|
|
51
|
+
## 0.5.0 - 2026-07-25
|
|
52
|
+
|
|
53
|
+
- Add an SSH-tunnel fallback for HANA Cloud hosts unreachable directly (e.g.
|
|
54
|
+
IP-allowlisted landscapes): on a classified connectivity failure, discover
|
|
55
|
+
a jump-host app in the same org/space via `cf apps`, open a local
|
|
56
|
+
`cf ssh -L` port-forward, and retry through it. Zero behavior change when
|
|
57
|
+
the direct connection succeeds.
|
|
58
|
+
- Add `--tunnel` to skip the direct attempt and connect via a tunnel
|
|
59
|
+
immediately, and `--refresh-tunnel` to bypass a cached/live tunnel and
|
|
60
|
+
force a fresh attempt. No disable switch: the fallback can only help or
|
|
61
|
+
no-op, and rethrows the original connection error unchanged on total
|
|
62
|
+
failure.
|
|
63
|
+
- Persist and reuse the live tunnel under `~/.saptools/cf-hana/tunnel/`
|
|
64
|
+
across every connection this CLI's pool opens and across separate
|
|
65
|
+
invocations run in a row against the same host, with a race-free
|
|
66
|
+
concurrent-establishment marker and automatic reaping of dead or
|
|
67
|
+
cross-org tunnels.
|
|
68
|
+
|
|
5
69
|
## 0.4.0 - 2026-07-14
|
|
6
70
|
|
|
7
71
|
- Back up `REPLACE` and matched `MERGE INTO` pre-images, cap backup size, and
|
package/README.md
CHANGED
|
@@ -105,7 +105,8 @@ cf-hana result <command> Inspect saved query refs
|
|
|
105
105
|
|
|
106
106
|
Common options: `--refresh` (deprecated compatibility flag; binding discovery is already live), `--role <runtime|hdi>`, `--binding <name>` /
|
|
107
107
|
`--binding-index <n>`, `--timeout <ms>`, `--read-only`, `--allow-destructive`,
|
|
108
|
-
`--limit <n>`, `--no-auto-limit
|
|
108
|
+
`--limit <n>`, `--no-auto-limit`, `--tunnel`, `--refresh-tunnel` (see
|
|
109
|
+
[Connectivity fallback](#connectivity-fallback)). The `query` command also accepts
|
|
109
110
|
`--param <value>` (repeatable), `--cell-limit <n>`, `--save`, `--no-auto-save`,
|
|
110
111
|
`--format <table|json|json-compact|csv>`, `--result-ttl-minutes <n>`, and
|
|
111
112
|
`--refresh-metadata`. `tables` and `columns` support the same four format values.
|
|
@@ -351,6 +352,95 @@ mode `0600`.
|
|
|
351
352
|
The guard is a convenience, not a security control: always pass values as bound
|
|
352
353
|
parameters.
|
|
353
354
|
|
|
355
|
+
## Connectivity fallback
|
|
356
|
+
|
|
357
|
+
HANA Cloud instances are frequently IP-allowlisted to only accept connections
|
|
358
|
+
from inside the same Cloud Foundry landscape. When a direct connection fails
|
|
359
|
+
in a way that means the initial socket could never be established — not an
|
|
360
|
+
authentication, privilege, or query failure — `cf-hana` can retry through an
|
|
361
|
+
SSH port-forward opened via `cf ssh` against another app in the same org/space,
|
|
362
|
+
which usually can reach the host even when your machine cannot.
|
|
363
|
+
|
|
364
|
+
- **`auto` (default)**: tries the direct connection first, with zero added
|
|
365
|
+
behavior when it succeeds. Only a classified connectivity failure triggers
|
|
366
|
+
the fallback: discover a jump-host app (the target app itself, then a few
|
|
367
|
+
other started apps via `cf apps`), open a local port-forward, and retry
|
|
368
|
+
through `127.0.0.1`.
|
|
369
|
+
- **`--tunnel`**: skips the direct attempt and connects via a tunnel
|
|
370
|
+
immediately — for a host already known to be unreachable directly, so you
|
|
371
|
+
are not paying the connect-timeout cost (up to 60s) on every invocation.
|
|
372
|
+
- **`--refresh-tunnel`**: bypasses a cached/live tunnel and forces a fresh
|
|
373
|
+
establishment attempt.
|
|
374
|
+
|
|
375
|
+
There is deliberately no flag to disable this capability: a tunnel attempt
|
|
376
|
+
only ever engages after a direct failure (or when explicitly requested via
|
|
377
|
+
`--tunnel`), and on total failure it rethrows the original connection error
|
|
378
|
+
unchanged, so it can only help or no-op.
|
|
379
|
+
|
|
380
|
+
The live tunnel is reused — both across every connection this CLI's own pool
|
|
381
|
+
opens in one invocation, and across separate `cf-hana` invocations run in a
|
|
382
|
+
row against the same host (this CLI's realistic dominant usage pattern, e.g.
|
|
383
|
+
an AI agent running several queries back to back) — instead of re-negotiating
|
|
384
|
+
SSH on every single command. State lives at `~/.saptools/cf-hana/tunnel/`
|
|
385
|
+
(mode `0700`/`0600`, no credentials). A cached tunnel is closed immediately,
|
|
386
|
+
regardless of its remaining lifetime, the moment a later invocation targets a
|
|
387
|
+
different Cloud Foundry org — a different client's landscape never keeps an
|
|
388
|
+
unattended live SSH path open just because its keepalive has not lapsed.
|
|
389
|
+
|
|
390
|
+
In the worst case (a silently-dropping network path, so the direct attempt
|
|
391
|
+
runs its full connect timeout, and no candidate app works either), `auto`
|
|
392
|
+
mode takes roughly 15s (direct, capped once a tunnel fallback is available —
|
|
393
|
+
see below) + 25s (tunnel budget) ≈ 40s before surfacing a final error —
|
|
394
|
+
bounded and predictable, not open-ended. Use `--tunnel` to skip the direct
|
|
395
|
+
cost entirely for a host you already know is unreachable.
|
|
396
|
+
|
|
397
|
+
Whenever a tunnel fallback is available at all (`auto` mode, the default),
|
|
398
|
+
the direct attempt's own timeout is capped at the tunnel side's own
|
|
399
|
+
per-candidate ceiling (15s by default) even if a longer timeout is
|
|
400
|
+
configured — a silently-hanging (rather than actively refused) direct
|
|
401
|
+
connection would otherwise consume its entire configured timeout before the
|
|
402
|
+
tunnel path got a chance at all. This only ever shortens an
|
|
403
|
+
otherwise-hanging attempt; it never affects a host that refuses quickly, or
|
|
404
|
+
a configured timeout that is already shorter.
|
|
405
|
+
|
|
406
|
+
`cf-hana` also disables SAP HANA Cloud's reactive mid-auth redirect (used
|
|
407
|
+
for per-node/pod locality routing) on every connection, direct or tunneled:
|
|
408
|
+
without this, a multi-node HANA Cloud instance can redirect an
|
|
409
|
+
already-established connection to a different internal hostname that isn't
|
|
410
|
+
reachable outside SAP's own network, silently abandoning a working SSH
|
|
411
|
+
tunnel for a fresh, untunneled connection that fails for the same reason the
|
|
412
|
+
original direct connection did. The original bound host is always a real,
|
|
413
|
+
working endpoint for your binding, so this has no effect on which schema,
|
|
414
|
+
user, or data you reach.
|
|
415
|
+
|
|
416
|
+
### Known limitations
|
|
417
|
+
|
|
418
|
+
- **Cross-org reaper and concurrent processes.** Every connection attempt
|
|
419
|
+
closes any cached tunnel tagged for a different Cloud Foundry org than the
|
|
420
|
+
current target, regardless of remaining lifetime — intentional hygiene so
|
|
421
|
+
switching orgs never leaves a stale tunnel open. On a shared machine, this
|
|
422
|
+
can also close another, unrelated, concurrently-running invocation's
|
|
423
|
+
active tunnel to a different org; there is no portable, low-cost way to
|
|
424
|
+
detect "is this port actively in use by someone else" from outside that
|
|
425
|
+
TCP session.
|
|
426
|
+
- **Stale-tunnel reaping trusts the recorded pid.** The reaper terminates a
|
|
427
|
+
dead or expired tunnel's process by its recorded pid, with no additional
|
|
428
|
+
identity check (e.g. confirming the pid still refers to a `cf ssh`
|
|
429
|
+
process). On a long-uptime machine, pid reuse could in principle target an
|
|
430
|
+
unrelated process; this is a narrow race not worth a platform-specific
|
|
431
|
+
check for.
|
|
432
|
+
- **Programmatic (non-CLI) pool usage can strand idle connections.** The
|
|
433
|
+
connection pool reuses the most-recently-released idle connection first
|
|
434
|
+
(LIFO); a long-lived process that opens a burst of connections and then
|
|
435
|
+
settles into serial one-at-a-time use can leave earlier connections
|
|
436
|
+
idle-but-unreaped for the pool's lifetime. The CLI itself always drains
|
|
437
|
+
its pool at the end of every command, so this only affects long-lived
|
|
438
|
+
programmatic use of the library.
|
|
439
|
+
- **Pre-write backups are never pruned.** Unlike SQL history (5-day
|
|
440
|
+
retention) and saved query results (TTL plus `result prune`/`result
|
|
441
|
+
clear`), files under `~/.saptools/cf-hana/backups/` accumulate
|
|
442
|
+
indefinitely with no retention policy or cleanup command.
|
|
443
|
+
|
|
354
444
|
## Requirements
|
|
355
445
|
|
|
356
446
|
- Node.js >= 20.
|