@reliabilityworks/vibesec 0.1.0 → 0.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.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +28 -2
- package/dist/cli.js.map +1 -1
- package/package.json +8 -7
- package/src/cli.ts +42 -2
- package/test/baseline.test.js +73 -0
- package/test/workspaceScan.test.js +73 -5
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAsMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA4G1D"}
|
package/dist/cli.js
CHANGED
|
@@ -109,7 +109,7 @@ async function loadRulesetRules(packageName) {
|
|
|
109
109
|
return parsed;
|
|
110
110
|
}
|
|
111
111
|
async function loadRulesForFrameworks(frameworks) {
|
|
112
|
-
const ids = new Set(frameworks.map((f) => f.id));
|
|
112
|
+
const ids = new Set(frameworks.map((f) => String(f.id)));
|
|
113
113
|
const rules = [];
|
|
114
114
|
if (ids.has('nextjs')) {
|
|
115
115
|
rules.push(...(await loadRulesetRules('@reliabilityworks/ruleset-nextjs')));
|
|
@@ -123,6 +123,9 @@ async function loadRulesForFrameworks(frameworks) {
|
|
|
123
123
|
if (ids.has('sveltekit')) {
|
|
124
124
|
rules.push(...(await loadRulesetRules('@reliabilityworks/ruleset-sveltekit')));
|
|
125
125
|
}
|
|
126
|
+
if (ids.has('astro')) {
|
|
127
|
+
rules.push(...(await loadRulesetRules('@reliabilityworks/ruleset-astro')));
|
|
128
|
+
}
|
|
126
129
|
return rules;
|
|
127
130
|
}
|
|
128
131
|
function readCliVersion() {
|
|
@@ -154,8 +157,9 @@ async function runCli(argv) {
|
|
|
154
157
|
.option('-o, --output <format>', 'Output format: cli|json|sarif|html', 'cli')
|
|
155
158
|
.option('--out-file <path>', 'Write output to file')
|
|
156
159
|
.option('--fail-on <severity>', 'Fail on or above: low|medium|high|critical', 'high')
|
|
157
|
-
.option('--framework <name>', 'Framework: auto|nextjs|react-native|expo|express|sveltekit (comma-separated)', 'auto')
|
|
160
|
+
.option('--framework <name>', 'Framework: auto|nextjs|react-native|expo|express|sveltekit|astro (comma-separated)', 'auto')
|
|
158
161
|
.option('--config <path>', 'Config file path (.vibesec.yaml by default)')
|
|
162
|
+
.option('--write-baseline [path]', 'Write baseline file (defaults to .vibesec.baseline.yaml in scan root)')
|
|
159
163
|
.option('--rules-dir <path>', 'Custom rules dir (.vibesec/rules by default)')
|
|
160
164
|
.action(async (scanPath, options) => {
|
|
161
165
|
const failOn = (0, core_1.severityFromString)(options.failOn);
|
|
@@ -169,6 +173,13 @@ async function runCli(argv) {
|
|
|
169
173
|
? await loadWorkspaceScopedRules(absoluteRoot)
|
|
170
174
|
: await loadRulesForFrameworks(frameworks)),
|
|
171
175
|
];
|
|
176
|
+
const writeBaselinePath = typeof options.writeBaseline === 'string'
|
|
177
|
+
? node_path_1.default.isAbsolute(options.writeBaseline)
|
|
178
|
+
? options.writeBaseline
|
|
179
|
+
: node_path_1.default.join(absoluteRoot, options.writeBaseline)
|
|
180
|
+
: options.writeBaseline
|
|
181
|
+
? node_path_1.default.join(absoluteRoot, '.vibesec.baseline.yaml')
|
|
182
|
+
: undefined;
|
|
172
183
|
const result = await (0, core_1.scanProject)({
|
|
173
184
|
rootDir: absoluteRoot,
|
|
174
185
|
frameworks,
|
|
@@ -176,6 +187,21 @@ async function runCli(argv) {
|
|
|
176
187
|
configPath: options.config,
|
|
177
188
|
customRulesDir: options.rulesDir,
|
|
178
189
|
});
|
|
190
|
+
if (writeBaselinePath) {
|
|
191
|
+
const ignore = result.findings
|
|
192
|
+
.map((finding) => ({ finding: finding.fingerprint, reason: 'baseline' }))
|
|
193
|
+
.sort((a, b) => a.finding.localeCompare(b.finding));
|
|
194
|
+
const lines = ['# Generated by vibesec', 'ignore:'];
|
|
195
|
+
for (const entry of ignore) {
|
|
196
|
+
lines.push(` - finding: ${entry.finding}`);
|
|
197
|
+
lines.push(` reason: ${entry.reason}`);
|
|
198
|
+
}
|
|
199
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(writeBaselinePath), { recursive: true });
|
|
200
|
+
await promises_1.default.writeFile(writeBaselinePath, lines.join('\n') + '\n', 'utf8');
|
|
201
|
+
process.stderr.write(`Wrote baseline with ${ignore.length} finding(s) to ${node_path_1.default.relative(process.cwd(), writeBaselinePath)}\n`);
|
|
202
|
+
process.exitCode = 0;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
179
205
|
if (options.output === 'json') {
|
|
180
206
|
const json = JSON.stringify(result, null, 2);
|
|
181
207
|
if (options.outFile) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reliabilityworks/vibesec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vibesec": "dist/cli.js"
|
|
6
6
|
},
|
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@reliabilityworks/analyzer-javascript": "0.
|
|
15
|
-
"@reliabilityworks/core": "0.
|
|
16
|
-
"@reliabilityworks/ruleset-
|
|
17
|
-
"@reliabilityworks/ruleset-
|
|
18
|
-
"@reliabilityworks/ruleset-
|
|
19
|
-
"@reliabilityworks/ruleset-
|
|
14
|
+
"@reliabilityworks/analyzer-javascript": "0.2.0",
|
|
15
|
+
"@reliabilityworks/core": "0.2.0",
|
|
16
|
+
"@reliabilityworks/ruleset-astro": "0.2.0",
|
|
17
|
+
"@reliabilityworks/ruleset-express": "0.2.0",
|
|
18
|
+
"@reliabilityworks/ruleset-nextjs": "0.2.0",
|
|
19
|
+
"@reliabilityworks/ruleset-react-native": "0.2.0",
|
|
20
|
+
"@reliabilityworks/ruleset-sveltekit": "0.2.0",
|
|
20
21
|
"commander": "^12.0.0"
|
|
21
22
|
}
|
|
22
23
|
}
|
package/src/cli.ts
CHANGED
|
@@ -28,6 +28,7 @@ type ScanCommandOptions = {
|
|
|
28
28
|
failOn: SeverityName
|
|
29
29
|
framework: string
|
|
30
30
|
config?: string
|
|
31
|
+
writeBaseline?: string | boolean
|
|
31
32
|
rulesDir?: string
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -149,7 +150,7 @@ async function loadRulesetRules(packageName: string): Promise<Rule[]> {
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
async function loadRulesForFrameworks(frameworks: FrameworkDetection[]): Promise<Rule[]> {
|
|
152
|
-
const ids = new Set(frameworks.map((f) => f.id))
|
|
153
|
+
const ids = new Set(frameworks.map((f) => String(f.id)))
|
|
153
154
|
const rules: Rule[] = []
|
|
154
155
|
|
|
155
156
|
if (ids.has('nextjs')) {
|
|
@@ -168,6 +169,10 @@ async function loadRulesForFrameworks(frameworks: FrameworkDetection[]): Promise
|
|
|
168
169
|
rules.push(...(await loadRulesetRules('@reliabilityworks/ruleset-sveltekit')))
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
if (ids.has('astro')) {
|
|
173
|
+
rules.push(...(await loadRulesetRules('@reliabilityworks/ruleset-astro')))
|
|
174
|
+
}
|
|
175
|
+
|
|
171
176
|
return rules
|
|
172
177
|
}
|
|
173
178
|
|
|
@@ -207,10 +212,14 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
207
212
|
.option('--fail-on <severity>', 'Fail on or above: low|medium|high|critical', 'high')
|
|
208
213
|
.option(
|
|
209
214
|
'--framework <name>',
|
|
210
|
-
'Framework: auto|nextjs|react-native|expo|express|sveltekit (comma-separated)',
|
|
215
|
+
'Framework: auto|nextjs|react-native|expo|express|sveltekit|astro (comma-separated)',
|
|
211
216
|
'auto',
|
|
212
217
|
)
|
|
213
218
|
.option('--config <path>', 'Config file path (.vibesec.yaml by default)')
|
|
219
|
+
.option(
|
|
220
|
+
'--write-baseline [path]',
|
|
221
|
+
'Write baseline file (defaults to .vibesec.baseline.yaml in scan root)',
|
|
222
|
+
)
|
|
214
223
|
.option('--rules-dir <path>', 'Custom rules dir (.vibesec/rules by default)')
|
|
215
224
|
.action(async (scanPath: string, options: ScanCommandOptions) => {
|
|
216
225
|
const failOn = severityFromString(options.failOn)
|
|
@@ -226,6 +235,15 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
226
235
|
: await loadRulesForFrameworks(frameworks)),
|
|
227
236
|
]
|
|
228
237
|
|
|
238
|
+
const writeBaselinePath =
|
|
239
|
+
typeof options.writeBaseline === 'string'
|
|
240
|
+
? path.isAbsolute(options.writeBaseline)
|
|
241
|
+
? options.writeBaseline
|
|
242
|
+
: path.join(absoluteRoot, options.writeBaseline)
|
|
243
|
+
: options.writeBaseline
|
|
244
|
+
? path.join(absoluteRoot, '.vibesec.baseline.yaml')
|
|
245
|
+
: undefined
|
|
246
|
+
|
|
229
247
|
const result = await scanProject({
|
|
230
248
|
rootDir: absoluteRoot,
|
|
231
249
|
frameworks,
|
|
@@ -234,6 +252,28 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
234
252
|
customRulesDir: options.rulesDir,
|
|
235
253
|
})
|
|
236
254
|
|
|
255
|
+
if (writeBaselinePath) {
|
|
256
|
+
const ignore = result.findings
|
|
257
|
+
.map((finding) => ({ finding: finding.fingerprint, reason: 'baseline' }))
|
|
258
|
+
.sort((a, b) => a.finding.localeCompare(b.finding))
|
|
259
|
+
|
|
260
|
+
const lines: string[] = ['# Generated by vibesec', 'ignore:']
|
|
261
|
+
for (const entry of ignore) {
|
|
262
|
+
lines.push(` - finding: ${entry.finding}`)
|
|
263
|
+
lines.push(` reason: ${entry.reason}`)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
await fs.mkdir(path.dirname(writeBaselinePath), { recursive: true })
|
|
267
|
+
await fs.writeFile(writeBaselinePath, lines.join('\n') + '\n', 'utf8')
|
|
268
|
+
|
|
269
|
+
process.stderr.write(
|
|
270
|
+
`Wrote baseline with ${ignore.length} finding(s) to ${path.relative(process.cwd(), writeBaselinePath)}\n`,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
process.exitCode = 0
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
|
|
237
277
|
if (options.output === 'json') {
|
|
238
278
|
const json = JSON.stringify(result, null, 2)
|
|
239
279
|
if (options.outFile) {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const { execFileSync } = require('node:child_process')
|
|
3
|
+
const fs = require('node:fs/promises')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const test = require('node:test')
|
|
7
|
+
|
|
8
|
+
async function writeFile(filePath, content) {
|
|
9
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
10
|
+
await fs.writeFile(filePath, content, 'utf8')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('scan --write-baseline suppresses findings on next run', async () => {
|
|
14
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'vibesec-baseline-'))
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const cliPath = path.join(__dirname, '..', 'dist', 'cli.js')
|
|
18
|
+
|
|
19
|
+
await writeFile(
|
|
20
|
+
path.join(tempRoot, 'next.config.js'),
|
|
21
|
+
'module.exports = {\n productionBrowserSourceMaps: true,\n}\n',
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
await writeFile(
|
|
25
|
+
path.join(tempRoot, 'src', 'next-rules.ts'),
|
|
26
|
+
'export const cfg = { revalidate: 0 }\nexport const v = process.env.NEXT_PUBLIC_API_SECRET\n',
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
execFileSync(
|
|
30
|
+
process.execPath,
|
|
31
|
+
[
|
|
32
|
+
'--enable-source-maps',
|
|
33
|
+
cliPath,
|
|
34
|
+
'scan',
|
|
35
|
+
tempRoot,
|
|
36
|
+
'--framework',
|
|
37
|
+
'nextjs',
|
|
38
|
+
'--fail-on',
|
|
39
|
+
'critical',
|
|
40
|
+
'--write-baseline',
|
|
41
|
+
],
|
|
42
|
+
{ encoding: 'utf8' },
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const baselinePath = path.join(tempRoot, '.vibesec.baseline.yaml')
|
|
46
|
+
const baselineRaw = await fs.readFile(baselinePath, 'utf8')
|
|
47
|
+
assert.ok(baselineRaw.includes('ignore:'))
|
|
48
|
+
assert.ok(baselineRaw.includes('finding:'))
|
|
49
|
+
|
|
50
|
+
const output = execFileSync(
|
|
51
|
+
process.execPath,
|
|
52
|
+
[
|
|
53
|
+
'--enable-source-maps',
|
|
54
|
+
cliPath,
|
|
55
|
+
'scan',
|
|
56
|
+
tempRoot,
|
|
57
|
+
'--framework',
|
|
58
|
+
'nextjs',
|
|
59
|
+
'--output',
|
|
60
|
+
'json',
|
|
61
|
+
'--fail-on',
|
|
62
|
+
'critical',
|
|
63
|
+
],
|
|
64
|
+
{ encoding: 'utf8' },
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const result = JSON.parse(output)
|
|
68
|
+
assert.equal(result.findings.length, 0)
|
|
69
|
+
assert.ok(result.ignoredFindings > 0)
|
|
70
|
+
} finally {
|
|
71
|
+
await fs.rm(tempRoot, { recursive: true, force: true })
|
|
72
|
+
}
|
|
73
|
+
})
|
|
@@ -3,6 +3,22 @@ const { execFileSync } = require('node:child_process')
|
|
|
3
3
|
const path = require('node:path')
|
|
4
4
|
const test = require('node:test')
|
|
5
5
|
|
|
6
|
+
function rulePaths(result, ruleId) {
|
|
7
|
+
return result.findings
|
|
8
|
+
.filter((finding) => finding.ruleId === ruleId)
|
|
9
|
+
.map((finding) => finding.location.path)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function assertAllPrefixed(paths, allowedPrefixes, ruleId) {
|
|
13
|
+
const prefixes = Array.isArray(allowedPrefixes) ? allowedPrefixes : [allowedPrefixes]
|
|
14
|
+
for (const p of paths) {
|
|
15
|
+
assert.ok(
|
|
16
|
+
prefixes.some((prefix) => p.startsWith(prefix)),
|
|
17
|
+
`${ruleId} should be scoped to ${prefixes.join(' or ')}, got ${p}`,
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
6
22
|
test('workspace scan scopes framework rules to project roots', async () => {
|
|
7
23
|
const fixtureRoot = path.join(__dirname, '..', '..', 'core', 'test', 'fixtures', 'monorepo')
|
|
8
24
|
const cliPath = path.join(__dirname, '..', 'dist', 'cli.js')
|
|
@@ -25,10 +41,62 @@ test('workspace scan scopes framework rules to project roots', async () => {
|
|
|
25
41
|
)
|
|
26
42
|
|
|
27
43
|
const result = JSON.parse(output)
|
|
28
|
-
const nextPaths = result.findings
|
|
29
|
-
.filter((finding) => finding.ruleId === 'nextjs/production-browser-sourcemaps')
|
|
30
|
-
.map((finding) => finding.location.path)
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
assert.
|
|
45
|
+
const nextSourcemaps = rulePaths(result, 'nextjs/production-browser-sourcemaps')
|
|
46
|
+
assert.ok(nextSourcemaps.includes('apps/web/next.config.js'))
|
|
47
|
+
assert.equal(nextSourcemaps.includes('apps/api/next.config.js'), false)
|
|
48
|
+
assertAllPrefixed(nextSourcemaps, 'apps/web/', 'nextjs/production-browser-sourcemaps')
|
|
49
|
+
|
|
50
|
+
const nextPublicSecret = rulePaths(result, 'nextjs/next-public-secret-name')
|
|
51
|
+
assert.ok(nextPublicSecret.includes('apps/web/src/next-rules.ts'))
|
|
52
|
+
assertAllPrefixed(nextPublicSecret, 'apps/web/', 'nextjs/next-public-secret-name')
|
|
53
|
+
|
|
54
|
+
const nextRevalidateZero = rulePaths(result, 'nextjs/unsafe-revalidate-zero')
|
|
55
|
+
assert.ok(nextRevalidateZero.includes('apps/web/src/next-rules.ts'))
|
|
56
|
+
assertAllPrefixed(nextRevalidateZero, 'apps/web/', 'nextjs/unsafe-revalidate-zero')
|
|
57
|
+
|
|
58
|
+
const corsWildcard = rulePaths(result, 'express/cors-wildcard-origin')
|
|
59
|
+
assert.ok(corsWildcard.includes('apps/api/express-rules.ts'))
|
|
60
|
+
assertAllPrefixed(corsWildcard, 'apps/api/', 'express/cors-wildcard-origin')
|
|
61
|
+
|
|
62
|
+
const allowOriginStar = rulePaths(result, 'express/allow-origin-star-header')
|
|
63
|
+
assert.ok(allowOriginStar.includes('apps/api/express-rules.ts'))
|
|
64
|
+
assertAllPrefixed(allowOriginStar, 'apps/api/', 'express/allow-origin-star-header')
|
|
65
|
+
|
|
66
|
+
const sessionCookieSecureFalse = rulePaths(result, 'express/session-cookie-secure-false')
|
|
67
|
+
assert.ok(sessionCookieSecureFalse.includes('apps/api/express-rules.ts'))
|
|
68
|
+
assertAllPrefixed(sessionCookieSecureFalse, 'apps/api/', 'express/session-cookie-secure-false')
|
|
69
|
+
|
|
70
|
+
const csrfCheckOrigin = rulePaths(result, 'sveltekit/csrf-checkorigin-false')
|
|
71
|
+
assert.ok(csrfCheckOrigin.includes('apps/kit/svelte.config.js'))
|
|
72
|
+
assertAllPrefixed(csrfCheckOrigin, 'apps/kit/', 'sveltekit/csrf-checkorigin-false')
|
|
73
|
+
|
|
74
|
+
const viteHostAll = rulePaths(result, 'sveltekit/vite-server-host-all')
|
|
75
|
+
assert.ok(viteHostAll.includes('apps/kit/vite.config.js'))
|
|
76
|
+
assertAllPrefixed(viteHostAll, 'apps/kit/', 'sveltekit/vite-server-host-all')
|
|
77
|
+
|
|
78
|
+
const astroSetHtml = rulePaths(result, 'astro/set-html')
|
|
79
|
+
assert.ok(astroSetHtml.includes('apps/astro/src/pages/index.astro'))
|
|
80
|
+
assert.equal(astroSetHtml.includes('apps/web/decoy.astro'), false)
|
|
81
|
+
assertAllPrefixed(astroSetHtml, 'apps/astro/', 'astro/set-html')
|
|
82
|
+
|
|
83
|
+
const astroInlineScript = rulePaths(result, 'astro/inline-script')
|
|
84
|
+
assert.ok(astroInlineScript.includes('apps/astro/src/pages/index.astro'))
|
|
85
|
+
assert.equal(astroInlineScript.includes('apps/web/decoy.astro'), false)
|
|
86
|
+
assertAllPrefixed(astroInlineScript, 'apps/astro/', 'astro/inline-script')
|
|
87
|
+
|
|
88
|
+
const rnManifestPaths = rulePaths(result, 'rn/cleartext-traffic')
|
|
89
|
+
assert.ok(rnManifestPaths.includes('apps/rn/android/app/AndroidManifest.xml'))
|
|
90
|
+
assert.equal(rnManifestPaths.includes('apps/web/android/app/AndroidManifest.xml'), false)
|
|
91
|
+
assertAllPrefixed(rnManifestPaths, 'apps/rn/', 'rn/cleartext-traffic')
|
|
92
|
+
|
|
93
|
+
const rnAtsPaths = rulePaths(result, 'rn/ats-arbitrary-loads')
|
|
94
|
+
assert.ok(rnAtsPaths.includes('apps/rn/Info.plist'))
|
|
95
|
+
assert.equal(rnAtsPaths.includes('apps/web/Info.plist'), false)
|
|
96
|
+
assertAllPrefixed(rnAtsPaths, 'apps/rn/', 'rn/ats-arbitrary-loads')
|
|
97
|
+
|
|
98
|
+
const expoTokenPaths = rulePaths(result, 'rn/asyncstorage-token-key')
|
|
99
|
+
assert.ok(expoTokenPaths.includes('apps/expo/src.ts'))
|
|
100
|
+
assert.equal(expoTokenPaths.includes('apps/web/src/decoy-rn.ts'), false)
|
|
101
|
+
assertAllPrefixed(expoTokenPaths, ['apps/expo/', 'apps/rn/'], 'rn/asyncstorage-token-key')
|
|
34
102
|
})
|