@sonisoft/now-sdk-ext-core 5.0.1 → 5.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.
Files changed (40) hide show
  1. package/dist/PublicApi.d.ts +8 -0
  2. package/dist/PublicApi.js +7 -0
  3. package/dist/PublicApi.js.map +1 -1
  4. package/dist/auth/NowSDKAuthenticationHandler.js +17 -3
  5. package/dist/auth/NowSDKAuthenticationHandler.js.map +1 -1
  6. package/dist/comm/http/IRequestHandler.d.ts +20 -1
  7. package/dist/comm/http/RequestHandler.d.ts +31 -1
  8. package/dist/comm/http/RequestHandler.js +90 -5
  9. package/dist/comm/http/RequestHandler.js.map +1 -1
  10. package/dist/comm/http/ServiceNowRequest.d.ts +2 -0
  11. package/dist/comm/http/ServiceNowRequest.js +9 -0
  12. package/dist/comm/http/ServiceNowRequest.js.map +1 -1
  13. package/dist/comm/http/SessionManager.d.ts +10 -0
  14. package/dist/comm/http/SessionManager.js +43 -1
  15. package/dist/comm/http/SessionManager.js.map +1 -1
  16. package/dist/credentials/ensureShim.d.ts +34 -0
  17. package/dist/credentials/ensureShim.js +71 -0
  18. package/dist/credentials/ensureShim.js.map +1 -0
  19. package/dist/exception/StaleInstanceError.d.ts +50 -0
  20. package/dist/exception/StaleInstanceError.js +63 -0
  21. package/dist/exception/StaleInstanceError.js.map +1 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/sn/BackgroundScriptExecutor.js +4 -1
  26. package/dist/sn/BackgroundScriptExecutor.js.map +1 -1
  27. package/dist/sn/IServiceNowInstance.d.ts +10 -0
  28. package/dist/sn/SNRequestBase.d.ts +13 -0
  29. package/dist/sn/SNRequestBase.js +17 -0
  30. package/dist/sn/SNRequestBase.js.map +1 -1
  31. package/dist/sn/ServiceNowInstance.d.ts +9 -0
  32. package/dist/sn/ServiceNowInstance.js +25 -0
  33. package/dist/sn/ServiceNowInstance.js.map +1 -1
  34. package/dist/util/Logger.d.ts +13 -0
  35. package/dist/util/Logger.js +30 -1
  36. package/dist/util/Logger.js.map +1 -1
  37. package/dist/util/redact.d.ts +47 -0
  38. package/dist/util/redact.js +228 -0
  39. package/dist/util/redact.js.map +1 -0
  40. package/package.json +4 -1
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Removes credential material from values before they are logged or thrown.
3
+ *
4
+ * The failure this exists for is not hypothetical. `RequestHandler.getRequestConfig`
5
+ * builds `{auth: this._session, ...}` and the next line logs that whole object at
6
+ * debug level, so every request wrote a live session — cookies, user token, or an
7
+ * OAuth access token — into `logs/app-debug.log`. That is what forced the `^logs/`
8
+ * allowlist in `.gitleaks.toml`.
9
+ *
10
+ * Redaction is by KEY, not by value. A value-based approach needs a registry of the
11
+ * secrets currently in play, which does not exist at the point a Winston format runs.
12
+ * Key-based matching costs nothing and catches the shape of the problem: secrets in
13
+ * this codebase always arrive under a recognisable name (`auth`, `cookie`,
14
+ * `access_token`, …) because they come from the SDK's own session objects.
15
+ */
16
+ export const REDACTED = "[redacted]";
17
+ /**
18
+ * Keys whose values must never be logged. Matched case-insensitively against the
19
+ * whole key, plus a substring pass for compound names like `sessionToken`.
20
+ */
21
+ const SECRET_KEYS = [
22
+ "auth",
23
+ "authorization",
24
+ "session",
25
+ "credential",
26
+ "credentials",
27
+ "password",
28
+ "passwd",
29
+ "secret",
30
+ "client_secret",
31
+ "clientsecret",
32
+ "token",
33
+ "access_token",
34
+ "refresh_token",
35
+ "id_token",
36
+ "usertoken",
37
+ "cookie",
38
+ "cookies",
39
+ "set-cookie",
40
+ "csrftoken",
41
+ "sysparm_ck",
42
+ "apikey",
43
+ "api_key",
44
+ ];
45
+ /** Substrings that make a key secret regardless of what surrounds them. */
46
+ const SECRET_FRAGMENTS = [
47
+ "password",
48
+ "secret",
49
+ "token",
50
+ "cookie",
51
+ "credential",
52
+ ];
53
+ /** Depth cap. Session and response graphs are deep; nothing useful lives past this. */
54
+ const MAX_DEPTH = 8;
55
+ export function isSecretKey(key) {
56
+ if (!key) {
57
+ return false;
58
+ }
59
+ const lowered = key.toLowerCase();
60
+ if (SECRET_KEYS.includes(lowered)) {
61
+ return true;
62
+ }
63
+ return SECRET_FRAGMENTS.some((fragment) => lowered.includes(fragment));
64
+ }
65
+ /**
66
+ * Returns a copy of `value` with every secret-shaped key replaced by `[redacted]`.
67
+ *
68
+ * Never mutates the input — callers are logging live objects that the rest of the
69
+ * request still depends on. Cycles, depth overruns, and unserializable values all
70
+ * degrade to a placeholder string rather than throwing: a logger that throws is worse
71
+ * than one that omits a field.
72
+ */
73
+ export function redactValue(value, depth = 0, seen) {
74
+ if (value === null || value === undefined) {
75
+ return value;
76
+ }
77
+ const primitive = typeof value !== "object" && typeof value !== "function";
78
+ if (primitive) {
79
+ return value;
80
+ }
81
+ if (depth >= MAX_DEPTH) {
82
+ return "[truncated]";
83
+ }
84
+ const tracker = seen ?? new WeakSet();
85
+ if (tracker.has(value)) {
86
+ return "[circular]";
87
+ }
88
+ tracker.add(value);
89
+ if (typeof value === "function") {
90
+ return "[function]";
91
+ }
92
+ if (value instanceof Error) {
93
+ return redactError(value, depth, tracker);
94
+ }
95
+ if (value instanceof Date) {
96
+ return value;
97
+ }
98
+ // tough-cookie CookieJar and similar: walking them yields the cookie values we are
99
+ // trying to suppress, and their internals are not useful in a log line anyway.
100
+ const constructorName = value?.constructor?.name ?? "";
101
+ if (/cookiejar|cookie/i.test(constructorName)) {
102
+ return REDACTED;
103
+ }
104
+ if (Array.isArray(value)) {
105
+ return value.map((entry) => redactValue(entry, depth + 1, tracker));
106
+ }
107
+ if (value instanceof Map) {
108
+ const out = {};
109
+ for (const [key, entry] of value.entries()) {
110
+ const name = String(key);
111
+ out[name] = isSecretKey(name) ? REDACTED : redactValue(entry, depth + 1, tracker);
112
+ }
113
+ return out;
114
+ }
115
+ if (value instanceof Set) {
116
+ return [...value].map((entry) => redactValue(entry, depth + 1, tracker));
117
+ }
118
+ const record = value;
119
+ const out = {};
120
+ for (const key of Object.keys(record)) {
121
+ if (isSecretKey(key)) {
122
+ out[key] = REDACTED;
123
+ continue;
124
+ }
125
+ try {
126
+ out[key] = redactValue(record[key], depth + 1, tracker);
127
+ }
128
+ catch {
129
+ // A throwing getter must not take the log line down with it.
130
+ out[key] = "[unreadable]";
131
+ }
132
+ }
133
+ return out;
134
+ }
135
+ /**
136
+ * Strips credential material from an error **in place** and returns it.
137
+ *
138
+ * Used before rethrowing, so the error that reaches a consumer carries nothing
139
+ * sensitive. Deliberately mutates rather than returning a replacement: consumers do
140
+ * `instanceof` checks against it (StaleInstanceError, and the CLI's remediation
141
+ * duck-typing), and rebuilding the error would break both and discard the stack.
142
+ *
143
+ * This matters more since sessions stopped being flattened at the throw sites: an
144
+ * error now reaches callers with `config` and `response` intact, and their loggers do
145
+ * not have core's redaction format.
146
+ */
147
+ export function stripSecretsFromError(error) {
148
+ if (!error || typeof error !== "object") {
149
+ return error;
150
+ }
151
+ const err = error;
152
+ for (const key of Object.keys(err)) {
153
+ if (isSecretKey(key)) {
154
+ err[key] = REDACTED;
155
+ continue;
156
+ }
157
+ const child = err[key];
158
+ if (child && typeof child === "object") {
159
+ try {
160
+ err[key] = redactValue(child);
161
+ }
162
+ catch {
163
+ err[key] = "[unreadable]";
164
+ }
165
+ }
166
+ }
167
+ // `cause` has to be handled by name. `new Error(msg, {cause})` installs it as a
168
+ // NON-ENUMERABLE own property, so the Object.keys loop above never sees it — and
169
+ // a wrapped network or auth failure is exactly the shape that carries a
170
+ // credential-bearing cause. Reassigned through the same descriptor so it stays
171
+ // non-enumerable and does not start showing up in JSON.stringify output.
172
+ if ("cause" in err) {
173
+ try {
174
+ const redactedCause = isSecretKey("cause") ? REDACTED : redactValue(err.cause);
175
+ Object.defineProperty(err, "cause", {
176
+ value: redactedCause,
177
+ writable: true,
178
+ enumerable: false,
179
+ configurable: true,
180
+ });
181
+ }
182
+ catch {
183
+ // A non-configurable cause cannot be replaced; leave it rather than throw
184
+ // from inside a function whose whole job is to make rethrowing safe.
185
+ }
186
+ }
187
+ return error;
188
+ }
189
+ /**
190
+ * Returns a plain object describing `error` with credential material removed.
191
+ *
192
+ * Kept structural rather than returning a rebuilt Error: this is for logging and for
193
+ * handing to a consumer, and a plain object cannot be mistaken for something throwable
194
+ * that has lost its prototype.
195
+ */
196
+ export function redactError(error, depth = 0, seen) {
197
+ const tracker = seen ?? new WeakSet();
198
+ const err = error;
199
+ const out = {
200
+ name: err?.name,
201
+ message: typeof err?.message === "string" ? err.message : String(err?.message ?? ""),
202
+ };
203
+ if (typeof err?.stack === "string") {
204
+ out.stack = err.stack;
205
+ }
206
+ // Everything else an error may be carrying — axios-style `config`, a fetch
207
+ // `request`, a `response`, a `cause` — is where credentials actually hide.
208
+ for (const key of Object.keys(err ?? {})) {
209
+ if (key === "name" || key === "message" || key === "stack") {
210
+ continue;
211
+ }
212
+ if (isSecretKey(key)) {
213
+ out[key] = REDACTED;
214
+ continue;
215
+ }
216
+ try {
217
+ out[key] = redactValue(err[key], depth + 1, tracker);
218
+ }
219
+ catch {
220
+ out[key] = "[unreadable]";
221
+ }
222
+ }
223
+ if (err?.cause !== undefined && out.cause === undefined) {
224
+ out.cause = redactValue(err.cause, depth + 1, tracker);
225
+ }
226
+ return out;
227
+ }
228
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/util/redact.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonisoft/now-sdk-ext-core",
3
- "version": "5.0.1",
3
+ "version": "5.2.0",
4
4
  "description": "Comprehensive TypeScript library extending ServiceNow SDK with application management, ATF testing, real-time log monitoring, AMB event subscriptions, and automation tools",
5
5
  "keywords": [
6
6
  "servicenow",
@@ -144,5 +144,8 @@
144
144
  },
145
145
  "engines": {
146
146
  "node": ">=26.0.0"
147
+ },
148
+ "optionalDependencies": {
149
+ "@sonisoft/sn-credstore": "^1.0.0"
147
150
  }
148
151
  }