react-native-nitro-logger 0.1.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/LICENSE +20 -0
- package/NitroLogger.podspec +29 -0
- package/README.md +214 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +124 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/AndroidPlatformIo.kt +100 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/HybridFileSink.kt +292 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/HybridNativeConsoleSink.kt +23 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogFileWriter.kt +1328 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogRotationPolicy.kt +186 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogSecureFile.kt +80 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/LogWriterRegistry.kt +420 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/NativeConsoleWriter.kt +237 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/NitroLoggerPackage.kt +22 -0
- package/android/src/main/java/com/margelo/nitro/nitrologger/PlatformIo.kt +165 -0
- package/docs/PARITY.md +330 -0
- package/docs/PRIVACY.md +341 -0
- package/eslint-plugin/README.md +238 -0
- package/eslint-plugin/index.js +62 -0
- package/eslint-plugin/rules/literal-subsystem.js +102 -0
- package/eslint-plugin/rules/no-computed-metadata-key.js +174 -0
- package/eslint-plugin/rules/no-derived-correlation.js +171 -0
- package/eslint-plugin/rules/no-dynamic-message.js +60 -0
- package/eslint-plugin/shared.js +1948 -0
- package/ios/Gzip.swift +203 -0
- package/ios/HybridFileSink.swift +291 -0
- package/ios/HybridNativeConsoleSink.swift +20 -0
- package/ios/LogFileWriter.swift +1280 -0
- package/ios/LogSecureFile.swift +296 -0
- package/ios/LogWriterRegistry.swift +444 -0
- package/ios/NativeConsoleWriter.swift +180 -0
- package/lib/module/Logger.js +381 -0
- package/lib/module/Logger.js.map +1 -0
- package/lib/module/ScopedLogger.js +70 -0
- package/lib/module/ScopedLogger.js.map +1 -0
- package/lib/module/config.js +23 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/destinations/Batcher.js +647 -0
- package/lib/module/destinations/Batcher.js.map +1 -0
- package/lib/module/destinations/ConsoleDestination.js +51 -0
- package/lib/module/destinations/ConsoleDestination.js.map +1 -0
- package/lib/module/destinations/FileDestination.js +360 -0
- package/lib/module/destinations/FileDestination.js.map +1 -0
- package/lib/module/destinations/NativeConsoleDestination.js +170 -0
- package/lib/module/destinations/NativeConsoleDestination.js.map +1 -0
- package/lib/module/destinations/types.js +4 -0
- package/lib/module/destinations/types.js.map +1 -0
- package/lib/module/formatters/DefaultFormatter.js +136 -0
- package/lib/module/formatters/DefaultFormatter.js.map +1 -0
- package/lib/module/formatters/JsonLinesFormatter.js +162 -0
- package/lib/module/formatters/JsonLinesFormatter.js.map +1 -0
- package/lib/module/formatters/timestamp.js +15 -0
- package/lib/module/formatters/timestamp.js.map +1 -0
- package/lib/module/formatters/types.js +4 -0
- package/lib/module/formatters/types.js.map +1 -0
- package/lib/module/index.js +31 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/integrations/appState.js +81 -0
- package/lib/module/integrations/appState.js.map +1 -0
- package/lib/module/integrations/errorHandler.js +166 -0
- package/lib/module/integrations/errorHandler.js.map +1 -0
- package/lib/module/integrations/sanitizeError.js +238 -0
- package/lib/module/integrations/sanitizeError.js.map +1 -0
- package/lib/module/levels.js +35 -0
- package/lib/module/levels.js.map +1 -0
- package/lib/module/metadata.js +52 -0
- package/lib/module/metadata.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/privacy.js +349 -0
- package/lib/module/privacy.js.map +1 -0
- package/lib/module/specs/FileSink.nitro.js +4 -0
- package/lib/module/specs/FileSink.nitro.js.map +1 -0
- package/lib/module/specs/NativeConsoleSink.nitro.js +4 -0
- package/lib/module/specs/NativeConsoleSink.nitro.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utf8.js +42 -0
- package/lib/module/utf8.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/Logger.d.ts +122 -0
- package/lib/typescript/src/Logger.d.ts.map +1 -0
- package/lib/typescript/src/ScopedLogger.d.ts +36 -0
- package/lib/typescript/src/ScopedLogger.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +12 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/destinations/Batcher.d.ts +244 -0
- package/lib/typescript/src/destinations/Batcher.d.ts.map +1 -0
- package/lib/typescript/src/destinations/ConsoleDestination.d.ts +27 -0
- package/lib/typescript/src/destinations/ConsoleDestination.d.ts.map +1 -0
- package/lib/typescript/src/destinations/FileDestination.d.ts +200 -0
- package/lib/typescript/src/destinations/FileDestination.d.ts.map +1 -0
- package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts +86 -0
- package/lib/typescript/src/destinations/NativeConsoleDestination.d.ts.map +1 -0
- package/lib/typescript/src/destinations/types.d.ts +19 -0
- package/lib/typescript/src/destinations/types.d.ts.map +1 -0
- package/lib/typescript/src/formatters/DefaultFormatter.d.ts +22 -0
- package/lib/typescript/src/formatters/DefaultFormatter.d.ts.map +1 -0
- package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts +73 -0
- package/lib/typescript/src/formatters/JsonLinesFormatter.d.ts.map +1 -0
- package/lib/typescript/src/formatters/timestamp.d.ts +6 -0
- package/lib/typescript/src/formatters/timestamp.d.ts.map +1 -0
- package/lib/typescript/src/formatters/types.d.ts +27 -0
- package/lib/typescript/src/formatters/types.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +34 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/integrations/appState.d.ts +33 -0
- package/lib/typescript/src/integrations/appState.d.ts.map +1 -0
- package/lib/typescript/src/integrations/errorHandler.d.ts +55 -0
- package/lib/typescript/src/integrations/errorHandler.d.ts.map +1 -0
- package/lib/typescript/src/integrations/sanitizeError.d.ts +89 -0
- package/lib/typescript/src/integrations/sanitizeError.d.ts.map +1 -0
- package/lib/typescript/src/levels.d.ts +9 -0
- package/lib/typescript/src/levels.d.ts.map +1 -0
- package/lib/typescript/src/metadata.d.ts +23 -0
- package/lib/typescript/src/metadata.d.ts.map +1 -0
- package/lib/typescript/src/privacy.d.ts +103 -0
- package/lib/typescript/src/privacy.d.ts.map +1 -0
- package/lib/typescript/src/specs/FileSink.nitro.d.ts +131 -0
- package/lib/typescript/src/specs/FileSink.nitro.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts +21 -0
- package/lib/typescript/src/specs/NativeConsoleSink.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +37 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utf8.d.ts +15 -0
- package/lib/typescript/src/utf8.d.ts.map +1 -0
- package/nitro.json +33 -0
- package/nitrogen/generated/android/c++/JAppendResult.hpp +79 -0
- package/nitrogen/generated/android/c++/JClearOutcome.hpp +88 -0
- package/nitrogen/generated/android/c++/JFlushOutcome.hpp +81 -0
- package/nitrogen/generated/android/c++/JHybridFileSinkSpec.cpp +120 -0
- package/nitrogen/generated/android/c++/JHybridFileSinkSpec.hpp +69 -0
- package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.cpp +71 -0
- package/nitrogen/generated/android/c++/JHybridNativeConsoleSinkSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JRejectReason.hpp +64 -0
- package/nitrogen/generated/android/c++/JRotationConfig.hpp +77 -0
- package/nitrogen/generated/android/c++/JSinkStatus.hpp +69 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/AppendResult.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/ClearOutcome.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/FlushOutcome.kt +81 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridFileSinkSpec.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/HybridNativeConsoleSinkSpec.kt +58 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RejectReason.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/RotationConfig.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/SinkStatus.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrologger/nitrologgerOnLoad.kt +35 -0
- package/nitrogen/generated/android/nitrologger+autolinking.cmake +83 -0
- package/nitrogen/generated/android/nitrologger+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitrologgerOnLoad.cpp +70 -0
- package/nitrogen/generated/android/nitrologgerOnLoad.hpp +34 -0
- package/nitrogen/generated/ios/NitroLogger+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.cpp +50 -0
- package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Bridge.hpp +216 -0
- package/nitrogen/generated/ios/NitroLogger-Swift-Cxx-Umbrella.hpp +69 -0
- package/nitrogen/generated/ios/NitroLoggerAutolinking.mm +41 -0
- package/nitrogen/generated/ios/NitroLoggerAutolinking.swift +38 -0
- package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridFileSinkSpecSwift.hpp +150 -0
- package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeConsoleSinkSpecSwift.hpp +87 -0
- package/nitrogen/generated/ios/swift/AppendResult.swift +60 -0
- package/nitrogen/generated/ios/swift/ClearOutcome.swift +50 -0
- package/nitrogen/generated/ios/swift/FlushOutcome.swift +59 -0
- package/nitrogen/generated/ios/swift/HybridFileSinkSpec.swift +61 -0
- package/nitrogen/generated/ios/swift/HybridFileSinkSpec_cxx.swift +227 -0
- package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridNativeConsoleSinkSpec_cxx.swift +148 -0
- package/nitrogen/generated/ios/swift/RejectReason.swift +48 -0
- package/nitrogen/generated/ios/swift/RotationConfig.swift +93 -0
- package/nitrogen/generated/ios/swift/SinkStatus.swift +44 -0
- package/nitrogen/generated/shared/c++/AppendResult.hpp +105 -0
- package/nitrogen/generated/shared/c++/ClearOutcome.hpp +96 -0
- package/nitrogen/generated/shared/c++/FlushOutcome.hpp +107 -0
- package/nitrogen/generated/shared/c++/HybridFileSinkSpec.cpp +28 -0
- package/nitrogen/generated/shared/c++/HybridFileSinkSpec.hpp +84 -0
- package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridNativeConsoleSinkSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/RejectReason.hpp +84 -0
- package/nitrogen/generated/shared/c++/RotationConfig.hpp +103 -0
- package/nitrogen/generated/shared/c++/SinkStatus.hpp +95 -0
- package/package.json +151 -0
- package/src/Logger.ts +417 -0
- package/src/ScopedLogger.ts +94 -0
- package/src/config.ts +25 -0
- package/src/destinations/Batcher.ts +805 -0
- package/src/destinations/ConsoleDestination.ts +60 -0
- package/src/destinations/FileDestination.ts +473 -0
- package/src/destinations/NativeConsoleDestination.ts +220 -0
- package/src/destinations/types.ts +19 -0
- package/src/formatters/DefaultFormatter.ts +150 -0
- package/src/formatters/JsonLinesFormatter.ts +195 -0
- package/src/formatters/timestamp.ts +12 -0
- package/src/formatters/types.ts +29 -0
- package/src/index.tsx +106 -0
- package/src/integrations/appState.ts +111 -0
- package/src/integrations/errorHandler.ts +223 -0
- package/src/integrations/sanitizeError.ts +287 -0
- package/src/levels.ts +35 -0
- package/src/metadata.ts +52 -0
- package/src/privacy.ts +389 -0
- package/src/specs/FileSink.nitro.ts +144 -0
- package/src/specs/NativeConsoleSink.nitro.ts +21 -0
- package/src/types.ts +45 -0
- package/src/utf8.ts +39 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
correlationArguments,
|
|
5
|
+
describeLogCall,
|
|
6
|
+
describeScopedCall,
|
|
7
|
+
isOmitted,
|
|
8
|
+
isTrustedLogger,
|
|
9
|
+
resolveVariable,
|
|
10
|
+
singleDef,
|
|
11
|
+
staticPropertyName,
|
|
12
|
+
unwrap,
|
|
13
|
+
} = require('../shared');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Correlation IDs are public by contract — they appear in every line of a
|
|
17
|
+
* scope — so they must be opaque and freshly random.
|
|
18
|
+
*
|
|
19
|
+
* The tempting shortcut is to reuse an identifier you already have: a
|
|
20
|
+
* patient ID, an MRN, a booking reference. That turns every log line in the
|
|
21
|
+
* scope into a re-identification key, and hashing does not fix it — a hash
|
|
22
|
+
* of a small identifier space is trivially reversible. This rule requires
|
|
23
|
+
* the ID to come from the logger's own `newCorrelationId()`.
|
|
24
|
+
*
|
|
25
|
+
* Provenance is checked, not spelling. A local
|
|
26
|
+
* `function newCorrelationId() { return patient.mrn }` produces exactly the
|
|
27
|
+
* leak the rule exists to prevent, so the generator has to be traceable to an
|
|
28
|
+
* import of the package that actually ships it.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Is this a call to the logger's `newCorrelationId`, and only that?
|
|
33
|
+
*
|
|
34
|
+
* Provenance must be exact here, not merely plausible. Everywhere else an
|
|
35
|
+
* ambiguous receiver widens what gets *checked*, which is safe; here it would
|
|
36
|
+
* widen what gets *trusted*, so `function f(logger) { Log.scoped(
|
|
37
|
+
* logger.newCorrelationId()) }` would launder any object's method into an
|
|
38
|
+
* approved ID generator. Only a receiver proven to be the Logger counts.
|
|
39
|
+
*/
|
|
40
|
+
function isCorrelationIdCall(context, node) {
|
|
41
|
+
const call = unwrap(node);
|
|
42
|
+
if (!call || call.type !== 'CallExpression') return false;
|
|
43
|
+
const callee = unwrap(call.callee);
|
|
44
|
+
if (!callee) return false;
|
|
45
|
+
|
|
46
|
+
// `Log.newCorrelationId()` — receiver must BE the Logger, not resemble it.
|
|
47
|
+
if (callee.type === 'MemberExpression') {
|
|
48
|
+
return (
|
|
49
|
+
staticPropertyName(context, callee) === 'newCorrelationId' &&
|
|
50
|
+
isTrustedLogger(context, callee.object)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (callee.type !== 'Identifier' || callee.name !== 'newCorrelationId') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// A bare call is trustworthy only when destructured off the Logger. The
|
|
59
|
+
// package exposes no standalone `newCorrelationId`, so accepting an import
|
|
60
|
+
// of that name would trust `import { newCorrelationId } from
|
|
61
|
+
// './phi-helpers'` — a function that can return anything at all.
|
|
62
|
+
const def = singleDef(resolveVariable(context, callee));
|
|
63
|
+
if (!def || def.type !== 'Variable') return false;
|
|
64
|
+
if (def.node.id.type !== 'ObjectPattern') return false;
|
|
65
|
+
// Which property it was destructured FROM is the whole point:
|
|
66
|
+
// `const { patientId: newCorrelationId } = Log` renames PHI into the
|
|
67
|
+
// approved spelling.
|
|
68
|
+
const property = def.node.id.properties.find((p) => {
|
|
69
|
+
if (p.type !== 'Property') return false;
|
|
70
|
+
const target =
|
|
71
|
+
p.value.type === 'AssignmentPattern' ? p.value.left : p.value;
|
|
72
|
+
return target === def.name;
|
|
73
|
+
});
|
|
74
|
+
if (
|
|
75
|
+
!property ||
|
|
76
|
+
property.computed ||
|
|
77
|
+
property.key.type !== 'Identifier' ||
|
|
78
|
+
property.key.name !== 'newCorrelationId'
|
|
79
|
+
) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return isTrustedLogger(context, def.node.init);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
meta: {
|
|
87
|
+
type: 'problem',
|
|
88
|
+
docs: {
|
|
89
|
+
description:
|
|
90
|
+
'require correlation IDs to come from newCorrelationId() rather than existing identifiers',
|
|
91
|
+
},
|
|
92
|
+
schema: [
|
|
93
|
+
{
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
loggerNames: { type: 'array', items: { type: 'string' } },
|
|
97
|
+
loggerModules: { type: 'array', items: { type: 'string' } },
|
|
98
|
+
singletonName: { type: 'string' },
|
|
99
|
+
},
|
|
100
|
+
additionalProperties: false,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
messages: {
|
|
104
|
+
derived:
|
|
105
|
+
'Correlation IDs must come from newCorrelationId(). Deriving one from a record, patient, or session identifier makes every line in the scope a re-identification key — and hashing a small identifier space does not anonymize it.',
|
|
106
|
+
unanalyzable:
|
|
107
|
+
'This correlation ID cannot be traced to newCorrelationId(). Pass the call directly, or a `const` initialized from it.',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
create(context) {
|
|
112
|
+
function check(node) {
|
|
113
|
+
if (!node) return;
|
|
114
|
+
// `Log.scoped(undefined)` auto-generates, exactly like `Log.scoped()`.
|
|
115
|
+
if (isOmitted(context, node)) return;
|
|
116
|
+
if (isCorrelationIdCall(context, node)) return;
|
|
117
|
+
|
|
118
|
+
// Follow a single-definition const to its initializer, so the common
|
|
119
|
+
// `const id = Log.newCorrelationId()` stays legal.
|
|
120
|
+
const current = unwrap(node);
|
|
121
|
+
if (current && current.type === 'Identifier') {
|
|
122
|
+
const variable = resolveVariable(context, current);
|
|
123
|
+
const def = singleDef(variable);
|
|
124
|
+
if (
|
|
125
|
+
def &&
|
|
126
|
+
def.type === 'Variable' &&
|
|
127
|
+
def.parent?.kind === 'const' &&
|
|
128
|
+
isCorrelationIdCall(context, def.node.init)
|
|
129
|
+
) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
context.report({ node, messageId: 'derived' });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
CallExpression(node) {
|
|
139
|
+
const scoped = describeScopedCall(context, node);
|
|
140
|
+
if (scoped) {
|
|
141
|
+
if (scoped.spreadArgs) {
|
|
142
|
+
// `Log.scoped(...args)` — the correlation position is unknowable,
|
|
143
|
+
// and whatever lands there tags every line of the scope.
|
|
144
|
+
context.report({ node, messageId: 'unanalyzable' });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// `scoped()` with no argument auto-generates — that is the
|
|
148
|
+
// encouraged form and needs no check.
|
|
149
|
+
if (scoped.args.length > 0) check(scoped.args[0]);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const call = describeLogCall(context, node);
|
|
154
|
+
if (!call) return;
|
|
155
|
+
if (call.spreadArgs && (call.method === 'log' || call.opaqueMethod)) {
|
|
156
|
+
// `Log.log(...['m', { correlation: patientId }])` — the options
|
|
157
|
+
// object, and the correlation inside it, are out of reach.
|
|
158
|
+
context.report({ node, messageId: 'unanalyzable' });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
for (const result of correlationArguments(context, call)) {
|
|
162
|
+
if (result.kind === 'unanalyzable') {
|
|
163
|
+
context.report({ node: result.node, messageId: 'unanalyzable' });
|
|
164
|
+
} else {
|
|
165
|
+
check(result.node);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { describeLogCall, isStaticMessage } = require('../shared');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The message is public by contract: the runtime cannot redact it, because
|
|
7
|
+
* it cannot tell a template's constant parts from its interpolated ones. So
|
|
8
|
+
* it has to be static at build time.
|
|
9
|
+
*
|
|
10
|
+
* `Log.info(\`patient ${id} admitted\`)` is the single most likely way PHI
|
|
11
|
+
* reaches a log file, and no amount of runtime validation catches it — by
|
|
12
|
+
* the time the logger sees the string, the interpolation already happened.
|
|
13
|
+
*/
|
|
14
|
+
module.exports = {
|
|
15
|
+
meta: {
|
|
16
|
+
type: 'problem',
|
|
17
|
+
docs: {
|
|
18
|
+
description:
|
|
19
|
+
'require log messages to be static strings, so interpolated data cannot reach a log line',
|
|
20
|
+
},
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
loggerNames: { type: 'array', items: { type: 'string' } },
|
|
26
|
+
loggerModules: { type: 'array', items: { type: 'string' } },
|
|
27
|
+
singletonName: { type: 'string' },
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
messages: {
|
|
33
|
+
dynamic:
|
|
34
|
+
'Log messages must be a string literal or an approved constant. Interpolated or computed text cannot be redacted at runtime — put the varying part in metadata, where the privacy layer can see it.',
|
|
35
|
+
unanalyzable:
|
|
36
|
+
'This call passes its arguments indirectly — a spread, an .apply(), or a partially applied .bind() — so the message position cannot be checked. Call the logging method directly.',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
create(context) {
|
|
41
|
+
return {
|
|
42
|
+
CallExpression(node) {
|
|
43
|
+
const call = describeLogCall(context, node);
|
|
44
|
+
if (!call) return;
|
|
45
|
+
|
|
46
|
+
if (call.spreadArgs) {
|
|
47
|
+
context.report({ node, messageId: 'unanalyzable' });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// A call with no arguments is someone else's error to report.
|
|
51
|
+
if (call.args.length === 0) return;
|
|
52
|
+
|
|
53
|
+
const message = call.args[0];
|
|
54
|
+
if (!isStaticMessage(context, message)) {
|
|
55
|
+
context.report({ node: message, messageId: 'dynamic' });
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|