forked from aws/agentcore-cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcredentials.ts
More file actions
164 lines (147 loc) · 4.2 KB
/
Copy pathcredentials.ts
File metadata and controls
164 lines (147 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* Secure credential management utilities.
* Prevents accidental exposure of sensitive data in logs, JSON serialization, and stack traces.
*/
/**
* A secure wrapper for credential data that prevents accidental exposure.
*
* Features:
* - Prevents JSON serialization of actual credential values
* - Returns redacted string in toString() for safe logging
* - Prevents iteration over credential keys in for...in loops
* - Immutable after creation
*
* @example
* ```ts
* const creds = new SecureCredentials({ OPENAI_API_KEY: 'sk-...' });
*
* // Safe to log - won't expose values
* console.log(creds); // "[SecureCredentials: 1 credential(s)]"
* console.log(JSON.stringify(creds)); // '{"_redacted":"[CREDENTIALS REDACTED]","count":1}'
*
* // Access values explicitly when needed
* const apiKey = creds.get('OPENAI_API_KEY');
*
* // Check if credential exists
* if (creds.has('OPENAI_API_KEY')) { ... }
* ```
*/
export class SecureCredentials {
private readonly credentials: Map<string, string>;
constructor(credentials: Record<string, string> = {}) {
this.credentials = new Map(Object.entries(credentials));
// Freeze to prevent modification
Object.freeze(this);
}
/**
* Get a credential value by key.
* Returns undefined if the key doesn't exist.
*/
get(key: string): string | undefined {
return this.credentials.get(key);
}
/**
* Check if a credential exists.
*/
has(key: string): boolean {
return this.credentials.has(key);
}
/**
* Get the number of stored credentials.
*/
get size(): number {
return this.credentials.size;
}
/**
* Check if any credentials are stored.
*/
isEmpty(): boolean {
return this.credentials.size === 0;
}
/**
* Get all credential keys (not values).
* Safe to log as it only returns key names.
*/
keys(): string[] {
return Array.from(this.credentials.keys());
}
/**
* Merge with another SecureCredentials instance or plain object.
* Returns a new SecureCredentials instance (immutable).
* The provided credentials take precedence over existing ones.
*/
merge(other: SecureCredentials | Record<string, string>): SecureCredentials {
const merged: Record<string, string> = {};
// Copy existing credentials
for (const key of this.credentials.keys()) {
const value = this.credentials.get(key);
if (value !== undefined) {
merged[key] = value;
}
}
// Overlay new credentials
if (other instanceof SecureCredentials) {
for (const key of other.keys()) {
const value = other.get(key);
if (value !== undefined) {
merged[key] = value;
}
}
} else {
for (const [key, value] of Object.entries(other)) {
if (value !== undefined) {
merged[key] = value;
}
}
}
return new SecureCredentials(merged);
}
/**
* Convert to plain object for internal use only.
* WARNING: This exposes credential values. Use only when passing to APIs that require plain objects.
*/
toPlainObject(): Record<string, string> {
const result: Record<string, string> = {};
for (const key of this.credentials.keys()) {
const value = this.credentials.get(key);
if (value !== undefined) {
result[key] = value;
}
}
return result;
}
/**
* Prevent JSON serialization from exposing credential values.
*/
toJSON(): { _redacted: string; count: number; keys: string[] } {
return {
_redacted: '[CREDENTIALS REDACTED]',
count: this.credentials.size,
keys: this.keys(),
};
}
/**
* Safe string representation for logging.
*/
toString(): string {
return `[SecureCredentials: ${this.credentials.size} credential(s)]`;
}
/**
* Custom inspect for Node.js console.log.
*/
[Symbol.for('nodejs.util.inspect.custom')](): string {
return this.toString();
}
/**
* Static factory to create from environment variables.
*/
static fromEnvVars(envVars: Record<string, string>): SecureCredentials {
return new SecureCredentials(envVars);
}
/**
* Static factory to create an empty SecureCredentials instance.
*/
static empty(): SecureCredentials {
return new SecureCredentials();
}
}