engineering-memory 1.11.1 → 1.11.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engineering-memory",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "Installs the Engineering Memory skill and its local MCP bridge. Sign in after installing; your organization and project are resolved from your account.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -4,7 +4,9 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { assertManagedPath, ensureManagedDirectory, readJson, removeFile, writeJson, } from '../utilities/files.js';
|
|
5
5
|
import { sha256, stableStringify } from '../utilities/hash.js';
|
|
6
6
|
const sensitiveKeyPattern = /(authorization|password|passcode|secret|token|cookie|email|phone|mobile|customer|account|user.?id|user.?name)/i;
|
|
7
|
-
const
|
|
7
|
+
const emailSource = '(?:^|[^\\p{L}\\p{N}._%+-])[\\p{L}\\p{N}._%+-]{1,64}@([\\p{L}\\p{N}.-]+\\.[\\p{L}]{2,63})(?=$|[^\\p{L}\\p{N}.-])';
|
|
8
|
+
const emailPattern = new RegExp(emailSource, 'iu');
|
|
9
|
+
const emailMatchPattern = new RegExp(emailSource, 'giu');
|
|
8
10
|
const phonePattern = /\+\d[\d ()-]{7,}\d/;
|
|
9
11
|
const repositoryPathFields = new Set([
|
|
10
12
|
'path',
|
|
@@ -13,21 +15,128 @@ const repositoryPathFields = new Set([
|
|
|
13
15
|
'changedPaths',
|
|
14
16
|
'pathPatterns',
|
|
15
17
|
'evidencePaths',
|
|
18
|
+
'screenPathPatterns',
|
|
19
|
+
'componentPathPatterns',
|
|
20
|
+
'pathChanges',
|
|
21
|
+
'templatePath',
|
|
16
22
|
]);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const topLevelDomains = new Set([
|
|
24
|
+
'com',
|
|
25
|
+
'net',
|
|
26
|
+
'org',
|
|
27
|
+
'edu',
|
|
28
|
+
'gov',
|
|
29
|
+
'mil',
|
|
30
|
+
'int',
|
|
31
|
+
'info',
|
|
32
|
+
'biz',
|
|
33
|
+
'name',
|
|
34
|
+
'pro',
|
|
35
|
+
'mobi',
|
|
36
|
+
'tel',
|
|
37
|
+
'asia',
|
|
38
|
+
'jobs',
|
|
39
|
+
'coop',
|
|
40
|
+
'aero',
|
|
41
|
+
'museum',
|
|
42
|
+
'travel',
|
|
43
|
+
'app',
|
|
44
|
+
'dev',
|
|
45
|
+
'cloud',
|
|
46
|
+
'online',
|
|
47
|
+
'site',
|
|
48
|
+
'store',
|
|
49
|
+
'tech',
|
|
50
|
+
'space',
|
|
51
|
+
'website',
|
|
52
|
+
'digital',
|
|
53
|
+
'agency',
|
|
54
|
+
'systems',
|
|
55
|
+
'solutions',
|
|
56
|
+
'email',
|
|
57
|
+
'xyz',
|
|
58
|
+
'top',
|
|
59
|
+
'club',
|
|
60
|
+
'live',
|
|
61
|
+
'life',
|
|
62
|
+
'world',
|
|
63
|
+
'today',
|
|
64
|
+
'network',
|
|
65
|
+
'group',
|
|
66
|
+
'company',
|
|
67
|
+
'center',
|
|
68
|
+
'media',
|
|
69
|
+
'studio',
|
|
70
|
+
'design',
|
|
71
|
+
'software',
|
|
72
|
+
'tools',
|
|
73
|
+
'works',
|
|
74
|
+
'team',
|
|
75
|
+
'zone',
|
|
76
|
+
'one',
|
|
77
|
+
'ltd',
|
|
78
|
+
'inc',
|
|
79
|
+
'llc',
|
|
80
|
+
'gmbh',
|
|
81
|
+
'shop',
|
|
82
|
+
'page',
|
|
83
|
+
'blog',
|
|
84
|
+
'wiki',
|
|
85
|
+
'news',
|
|
86
|
+
'art',
|
|
87
|
+
'link',
|
|
88
|
+
'host',
|
|
89
|
+
'press',
|
|
90
|
+
'fyi',
|
|
91
|
+
'test',
|
|
92
|
+
'example',
|
|
93
|
+
'invalid',
|
|
94
|
+
'local',
|
|
95
|
+
'localhost',
|
|
96
|
+
'internal',
|
|
97
|
+
]);
|
|
98
|
+
const countryCodeTopLevelDomains = new Set('ac ad ae af ag ai al am ao aq ar as at au aw ax az ba bb bd be bf bg bh bi bj bm bn bo br bs bt bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv cw cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gd ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sk sl sm sn so sr ss st su sv sx sy sz tc td tf tg th tj tk tl tm tn to tr tt tv tw tz ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw'.split(' '));
|
|
99
|
+
function isTopLevelDomain(label) {
|
|
100
|
+
const value = label.toLowerCase();
|
|
101
|
+
return countryCodeTopLevelDomains.has(value) || topLevelDomains.has(value);
|
|
102
|
+
}
|
|
103
|
+
const extensionTopLevelDomains = new Set([
|
|
104
|
+
'ai',
|
|
105
|
+
'app',
|
|
106
|
+
'as',
|
|
107
|
+
'cc',
|
|
108
|
+
'md',
|
|
109
|
+
'ml',
|
|
110
|
+
'mm',
|
|
111
|
+
'pl',
|
|
112
|
+
'py',
|
|
113
|
+
'rs',
|
|
114
|
+
'sh',
|
|
115
|
+
'so',
|
|
116
|
+
]);
|
|
117
|
+
function hasEmailAddress(value, repositoryPath) {
|
|
118
|
+
if (!repositoryPath)
|
|
119
|
+
return emailPattern.test(value);
|
|
120
|
+
for (const match of value.matchAll(emailMatchPattern)) {
|
|
121
|
+
const labels = (match[1] ?? '').split('.');
|
|
122
|
+
const last = (labels[labels.length - 1] ?? '').toLowerCase();
|
|
123
|
+
if (isTopLevelDomain(last) && !extensionTopLevelDomains.has(last))
|
|
124
|
+
return true;
|
|
125
|
+
const beforeExtension = (labels[labels.length - 2] ?? '').toLowerCase();
|
|
126
|
+
if (labels.length >= 3 &&
|
|
127
|
+
topLevelDomains.has(beforeExtension) &&
|
|
128
|
+
!extensionTopLevelDomains.has(beforeExtension))
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
24
132
|
}
|
|
133
|
+
const knownCredentialPattern = /\b(?:A(?:KI|SI)A[0-9A-Z]{16}|gh[pousr]_[A-Z0-9]{30,}|github_pat_[A-Z0-9_]{30,}|sk_live_[A-Z0-9]{16,}|rk_live_[A-Z0-9]{16,}|xox[baprs]-[A-Z0-9-]{12,}|AIza[A-Z0-9_-]{35}|sk-(?:proj-)?[A-Z0-9_-]{20,})\b/i;
|
|
25
134
|
const bearerPattern = /\bbearer\s+[a-z0-9._~-]+/i;
|
|
26
135
|
const jwtPattern = /\beyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/;
|
|
27
136
|
const privateKeyPattern = /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/;
|
|
28
137
|
const credentialUrlPattern = /\b[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:[^\s/@]+@/i;
|
|
29
138
|
const ipv4Pattern = /\b(?:\d{1,3}\.){3}\d{1,3}\b/;
|
|
30
|
-
const personalHomePathPattern = /(
|
|
139
|
+
const personalHomePathPattern = /(?:[A-Z]:[\\/](?:Users|Documents and Settings)[\\/][^\\/\s"'<>:|?*]+|(?:^|[\s"'(<=])\/(?:Users|home)\/[^/\s"'<>*]+)(?:[\\/]|$)/i;
|
|
31
140
|
export class OfflineOutbox {
|
|
32
141
|
root;
|
|
33
142
|
queues = new Map();
|
|
@@ -171,15 +280,17 @@ export function assertSafeToPersist(value, key = '') {
|
|
|
171
280
|
throw new Error(`Sensitive field cannot be persisted: ${key}`);
|
|
172
281
|
}
|
|
173
282
|
if (typeof value === 'string') {
|
|
174
|
-
|
|
283
|
+
const repositoryPath = repositoryPathFields.has(key);
|
|
284
|
+
if (hasEmailAddress(value, repositoryPath) ||
|
|
175
285
|
phonePattern.test(value) ||
|
|
176
286
|
bearerPattern.test(value) ||
|
|
177
287
|
jwtPattern.test(value) ||
|
|
178
288
|
privateKeyPattern.test(value) ||
|
|
289
|
+
knownCredentialPattern.test(value) ||
|
|
179
290
|
credentialUrlPattern.test(value) ||
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
throw new Error(
|
|
291
|
+
personalHomePathPattern.test(value) ||
|
|
292
|
+
(!repositoryPath && ipv4Pattern.test(value))) {
|
|
293
|
+
throw new Error(`PII or credentials cannot be persisted: ${key} ${value}`);
|
|
183
294
|
}
|
|
184
295
|
return;
|
|
185
296
|
}
|