update depedencies, changes to support updated authentication-module
[squeep-indie-auther] / src / logger / data-sanitizers.js
1 'use strict';
2
3 /**
4 * Scrub credential from POST login body data.
5 * @param {Object} data
6 * @param {Boolean} sanitize
7 * @returns {Boolean}
8 */
9 function sanitizePostCredential(data, sanitize = true) {
10 let unclean = false;
11
12 [
13 'credential',
14 'credential-old',
15 'credential-new',
16 'credential-new-2',
17 ].forEach((k) => {
18 const credentialLength = data?.ctx?.parsedBody?.[k]?.length; // eslint-disable-line security/detect-object-injection
19 const kUnclean = !!credentialLength;
20 unclean |= kUnclean;
21 if (kUnclean && sanitize) {
22 data.ctx.parsedBody[k] = '*'.repeat(credentialLength); // eslint-disable-line security/detect-object-injection
23 }
24 });
25
26 return unclean;
27 }
28
29
30 /**
31 * Scrub sensitive data from context.
32 * @param {Object} data
33 * @param {Boolean} sanitize
34 * @returns {Boolean}
35 */
36 function sanitizeContext(data, sanitize = true) {
37 let unclean = false;
38
39 // hide keys
40 [
41 'otpKey',
42 'otpConfirmKey',
43 ].forEach((k) => {
44 const secretLength = data?.ctx?.[k]?.length; // eslint-disable-line security/detect-object-injection
45 const kUnclean = !! secretLength;
46 unclean |= kUnclean;
47 if (kUnclean && sanitize) {
48 data.ctx[k] = '*'.repeat(secretLength); // eslint-disable-line security/detect-object-injection
49 }
50 });
51
52 // shorten mystery boxes
53 [
54 'otpConfirmBox',
55 'otpState',
56 ].forEach((k) => {
57 const mysteryLength = data?.ctx?.[k]?.length; // eslint-disable-line security/detect-object-injection
58 const mUnclean = !! mysteryLength;
59 unclean |= mUnclean;
60 if (mUnclean && sanitize) {
61 data.ctx[k] = `[scrubbed ${mysteryLength} bytes]`; // eslint-disable-line security/detect-object-injection
62 }
63 });
64
65 const cookieLength = data?.ctx?.cookie?.squeepSession?.length;
66 if (cookieLength) {
67 unclean |= true;
68 if (sanitize) {
69 data.ctx.cookie.squeepSession = `[scrubbed ${cookieLength} bytes]`;
70 }
71 }
72
73 return !! unclean;
74 }
75
76
77 /**
78 * Reduce logged data about scopes from profilesScopes.
79 * For all referenced scopes, only include profiles list.
80 * Remove scopes without profile references from scopeIndex.
81 * @param {Object} data
82 * @param {Boolean} sanitize
83 */
84 function reduceScopeVerbosity(data, sanitize = true) {
85 let unclean = false;
86
87 const {
88 scopesEntries: ctxScopesEntries,
89 profilesEntries: ctxProfilesEntries,
90 needsSanitize: ctxNeedsSanitize,
91 } = _scopesFrom(data?.ctx?.profilesScopes);
92
93 const {
94 scopesEntries: sessionScopesEntries,
95 profilesEntries: sessionProfilesEntries,
96 needsSanitize: sessionNeedsSanitize,
97 } = _scopesFrom(data?.ctx?.session);
98
99 if (ctxNeedsSanitize || sessionNeedsSanitize) {
100 unclean = true;
101 }
102 if (unclean && sanitize) {
103 if (ctxNeedsSanitize) {
104 Object.assign(data.ctx.profilesScopes, _sanitizeProfilesScopes(ctxScopesEntries, ctxProfilesEntries));
105 }
106 if (sessionNeedsSanitize) {
107 Object.assign(data.ctx.session, _sanitizeProfilesScopes(sessionScopesEntries, sessionProfilesEntries));
108 }
109 }
110
111 return unclean;
112 }
113
114
115 /**
116 * Return any scope entries on an object, and whether sanitization is needed.
117 * @param {Object=} obj
118 * @returns {Object}
119 */
120 const _scopesFrom = (obj) => {
121 const scopesEntries = Object.entries(obj?.scopeIndex || {});
122 const profilesEntries = Object.entries(obj?.profileScopes || {});
123 const needsSanitize = scopesEntries.length || profilesEntries.length;
124 return {
125 scopesEntries,
126 profilesEntries,
127 needsSanitize,
128 };
129 };
130
131
132 /**
133 * @typedef {[String, Object]} ScopeEntry
134 */
135 /**
136 * Return new list of entries with scrubbed scopeDetails.
137 * @param {ScopeEntry[]} entries
138 * @returns {ScopeEntry[]}
139 */
140 const _scopeEntriesScrubber = (entries) => entries.map(([scopeName, scopeDetails]) => ([scopeName, { profiles: scopeDetails.profiles }]));
141
142
143 /**
144 * Create a new profilesScopes type object with scrubbed scope details.
145 * @param {ScopeEntry[]} scopesEntries
146 * @param {ScopeEntry[]} profilesEntries
147 * @returns {Object}
148 */
149 const _sanitizeProfilesScopes = (scopesEntries, profilesEntries) => {
150 const referencedScopesEntries = scopesEntries.filter(([_scopeName, scopeDetails]) => scopeDetails?.profiles?.length); // eslint-disable-line no-unused-vars
151 const scrubbedScopesEntries = _scopeEntriesScrubber(referencedScopesEntries);
152
153 const scrubbedProfilesEntries = profilesEntries.map(([profileName, profileScopes]) => {
154 const profileScopeEntries = Object.entries(profileScopes);
155 const scrubbedProfileScopeEntries = _scopeEntriesScrubber(profileScopeEntries);
156 const scrubbedProfileScopes = Object.fromEntries(scrubbedProfileScopeEntries);
157 return [profileName, scrubbedProfileScopes];
158 });
159
160 return {
161 scopeIndex: Object.fromEntries(scrubbedScopesEntries),
162 profileScopes: Object.fromEntries(scrubbedProfilesEntries),
163 };
164 };
165
166 module.exports = {
167 sanitizePostCredential,
168 sanitizeContext,
169 reduceScopeVerbosity,
170 };