funifier-mcp 0.3.17 → 0.3.18

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.
Files changed (40) hide show
  1. package/.cursor/rules/funifier.mdc +1 -0
  2. package/.github/copilot-instructions.md +1 -0
  3. package/AGENTS.md +1 -0
  4. package/datasource-funifier-docs/.coverage.json +12 -5
  5. package/datasource-funifier-docs/.search-index.json +10705 -10118
  6. package/datasource-funifier-docs/.skills-map.json +4 -0
  7. package/datasource-funifier-docs/.validation.json +72 -36
  8. package/datasource-funifier-docs/knowledge/guides/permission-audit.md +229 -0
  9. package/datasource-funifier-docs/knowledge/index.md +1 -0
  10. package/dist/mcp/bundle.js +108 -108
  11. package/dist/mcp/tools/_audit.d.ts +103 -0
  12. package/dist/mcp/tools/_audit.d.ts.map +1 -0
  13. package/dist/mcp/tools/_audit.js +241 -0
  14. package/dist/mcp/tools/_audit.js.map +1 -0
  15. package/dist/mcp/tools/_audit.test.d.ts +2 -0
  16. package/dist/mcp/tools/_audit.test.d.ts.map +1 -0
  17. package/dist/mcp/tools/_audit.test.js +412 -0
  18. package/dist/mcp/tools/_audit.test.js.map +1 -0
  19. package/dist/mcp/tools/_backup.d.ts +37 -3
  20. package/dist/mcp/tools/_backup.d.ts.map +1 -1
  21. package/dist/mcp/tools/_backup.js +137 -8
  22. package/dist/mcp/tools/_backup.js.map +1 -1
  23. package/dist/mcp/tools/_backup.test.js +195 -0
  24. package/dist/mcp/tools/_backup.test.js.map +1 -1
  25. package/dist/mcp/tools/_scope-engine.d.ts +40 -0
  26. package/dist/mcp/tools/_scope-engine.d.ts.map +1 -0
  27. package/dist/mcp/tools/_scope-engine.js +197 -0
  28. package/dist/mcp/tools/_scope-engine.js.map +1 -0
  29. package/dist/mcp/tools/_scope-engine.test.d.ts +2 -0
  30. package/dist/mcp/tools/_scope-engine.test.d.ts.map +1 -0
  31. package/dist/mcp/tools/_scope-engine.test.js +241 -0
  32. package/dist/mcp/tools/_scope-engine.test.js.map +1 -0
  33. package/dist/mcp/tools/permissions.d.ts.map +1 -1
  34. package/dist/mcp/tools/permissions.js +68 -11
  35. package/dist/mcp/tools/permissions.js.map +1 -1
  36. package/dist/mcp/tools/permissions.test.js +268 -4
  37. package/dist/mcp/tools/permissions.test.js.map +1 -1
  38. package/package.json +1 -1
  39. package/skills/funifier/SKILL.md +1 -0
  40. package/skills/funifier/references/audit-permissions.md +89 -0
@@ -137,5 +137,9 @@
137
137
  ],
138
138
  "funifier-manage-indexes": [
139
139
  "knowledge/modules/database.md"
140
+ ],
141
+ "funifier-audit-permissions": [
142
+ "knowledge/guides/permission-audit.md",
143
+ "knowledge/modules/security.md"
140
144
  ]
141
145
  }
@@ -1,13 +1,49 @@
1
1
  {
2
- "generatedAt": "2026-06-12T12:18:00Z",
2
+ "generatedAt": "2026-06-12T16:08:00Z",
3
3
  "skills": {
4
+ "funifier-audit-permissions": {
5
+ "configHash": "f0569bdb0e11e1a2c6a11f6a4ff9cfd940668c7431bee06342b29faab32510fc",
6
+ "status": "verified",
7
+ "claims": [
8
+ {
9
+ "claim": "SecurityFilter.doFilter evaluates (method, path) by computing verb (GET→read, POST/PUT→write, DELETE→delete) and entity_full (path after /v3/ with slash→underscore), then matching scope tokens in order: {verb}_all, exact {verb}_{entity_full}, fallback {verb}_{prefix}_all minus-1 and minus-2 trailing segments",
10
+ "gitnexusQuery": "SecurityFilter",
11
+ "critical": true,
12
+ "status": "verified",
13
+ "evidence": {
14
+ "symbol": "Class:src/main/java/com/funifier/rest/v3/filter/SecurityFilter.java:SecurityFilter",
15
+ "file": "src/main/java/com/funifier/rest/v3/filter/SecurityFilter.java"
16
+ }
17
+ },
18
+ {
19
+ "claim": "DatabaseRest requires the literal 'database' scope keyword on ALL /v3/database handlers including GET operations — grammar tokens such as read_all alone are insufficient; this is enforced within SecurityFilter.doFilter before requests reach DatabaseRest handlers",
20
+ "gitnexusQuery": "doFilter",
21
+ "critical": true,
22
+ "status": "verified",
23
+ "evidence": {
24
+ "symbol": "Method:src/main/java/com/funifier/rest/v3/filter/SecurityFilter.java:SecurityFilter.doFilter#3",
25
+ "file": "src/main/java/com/funifier/rest/v3/filter/SecurityFilter.java"
26
+ }
27
+ },
28
+ {
29
+ "claim": "Bearer tokens embed the player's role scope string at login time; role scope changes only affect sessions created after the change — existing live Bearer tokens carry the scope at their issuance time",
30
+ "gitnexusQuery": "SecurityFilter",
31
+ "critical": true,
32
+ "status": "verified",
33
+ "evidence": {
34
+ "symbol": "Class:src/main/java/com/funifier/rest/v3/filter/SecurityFilter.java:SecurityFilter",
35
+ "file": "src/main/java/com/funifier/rest/v3/filter/SecurityFilter.java"
36
+ }
37
+ }
38
+ ]
39
+ },
4
40
  "funifier-configure-security": {
5
- "configHash": "fba0f846a53838e369f5c079104cd4a2f16a9d535a0320701d16287e53f35496",
41
+ "configHash": "a05c3dc778b494d2d9d781787dd2231c1ae5720c3e368b41071f02df85a1a922",
6
42
  "status": "verified",
7
43
  "claims": []
8
44
  },
9
45
  "funifier-create-action": {
10
- "configHash": "2b0f8eddb8f942fc407c1d19b15fdc80cb7d9a1e01c1093befdfd3c483c1cb68",
46
+ "configHash": "378dcd46c1dbad66e0363790d65d5bc0bba9ea3b294a97b571cc6d87c26ddf11",
11
47
  "status": "verified",
12
48
  "claims": [
13
49
  {
@@ -33,7 +69,7 @@
33
69
  ]
34
70
  },
35
71
  "funifier-create-aggregate": {
36
- "configHash": "9598173385df039c369c8efc036ec22faaa3f9e56cb6c787498bf3d72b18a634",
72
+ "configHash": "1c241176306556b5f4e626f49bb2961fa060678b83f0f9ef72d862249f3e6918",
37
73
  "status": "verified",
38
74
  "claims": [
39
75
  {
@@ -49,7 +85,7 @@
49
85
  ]
50
86
  },
51
87
  "funifier-create-audit": {
52
- "configHash": "17d53b67ecf952aaa352f0799e465d00de59960cd51cea31dee6f5d9ad5f5c8f",
88
+ "configHash": "0ca754be3545a16a6efb50ca2c4b360f1438efa1cd1bc2b5109aa76768376e07",
53
89
  "status": "verified",
54
90
  "claims": [
55
91
  {
@@ -85,7 +121,7 @@
85
121
  ]
86
122
  },
87
123
  "funifier-create-challenge": {
88
- "configHash": "45e50eab442325d3342dddff5d7c48f34002fe0182ed78c31a48fffdb8eafd03",
124
+ "configHash": "7065294da9c53a9d1bbe544749ff552d6674d0d77e2534ea17e43fae4c72474c",
89
125
  "status": "verified",
90
126
  "claims": [
91
127
  {
@@ -111,7 +147,7 @@
111
147
  ]
112
148
  },
113
149
  "funifier-create-competition": {
114
- "configHash": "ed7fe6dc128ebe0cbb417544d2f25209cf472a11fd38e4b78bb33b7662e54618",
150
+ "configHash": "0de8bcda95d0a1764ef4ce7aa7e78c53c7f7d492012534ecaf7f87600c42f078",
115
151
  "status": "verified",
116
152
  "claims": [
117
153
  {
@@ -137,7 +173,7 @@
137
173
  ]
138
174
  },
139
175
  "funifier-create-crossword": {
140
- "configHash": "c1e731de20525b88b05c1f644c196fa5aa31c593a75f7f03de8c8030564ab5fb",
176
+ "configHash": "63ebc6f1e0589aaa748464e54b3dc3460094fa41764922534b6017288fc79541",
141
177
  "status": "verified",
142
178
  "claims": [
143
179
  {
@@ -153,7 +189,7 @@
153
189
  ]
154
190
  },
155
191
  "funifier-create-custom-object": {
156
- "configHash": "58677f7f00c4d432e792f825bbc12b11e61722057c143d4a291da600d5beb758",
192
+ "configHash": "8aa7a96fdc72793c3d095db04831396d58a1aca931a7542cc8d5b8eed2e0c31d",
157
193
  "status": "verified",
158
194
  "claims": [
159
195
  {
@@ -169,7 +205,7 @@
169
205
  ]
170
206
  },
171
207
  "funifier-create-custom-page": {
172
- "configHash": "d7a517d522858c659e5ead012c9f0594829aee22d96cda561536fbb803cd5db0",
208
+ "configHash": "7eac6647589aee0ca03e2fc213dac98a212ef0100e2f69da38cb758f7f65a7e7",
173
209
  "status": "verified",
174
210
  "claims": [
175
211
  {
@@ -185,7 +221,7 @@
185
221
  ]
186
222
  },
187
223
  "funifier-create-folder": {
188
- "configHash": "ec7b1e3fadcd07d89f271f1bc10a28f820916fe8c0e645da88d48a43db868966",
224
+ "configHash": "16b0c0ae4ddebd51a16a0f330a80bed8c186cd7a760a9e52a5fb16d3cf6e462f",
189
225
  "status": "verified",
190
226
  "claims": [
191
227
  {
@@ -211,7 +247,7 @@
211
247
  ]
212
248
  },
213
249
  "funifier-create-lastmile": {
214
- "configHash": "176113c689332843b24d22f7705d1361dfabf3e369de816b59b33a86ed0d9041",
250
+ "configHash": "e769c3ad773a9069ae41902d52195b81cfaaf6eb61cc15fb1d443c95ae512e55",
215
251
  "status": "verified",
216
252
  "claims": [
217
253
  {
@@ -227,7 +263,7 @@
227
263
  ]
228
264
  },
229
265
  "funifier-create-leaderboard": {
230
- "configHash": "1cd6bca4e73fda88a8a4e965af46f45acbba2508098557f321819fbe04d57fb3",
266
+ "configHash": "f0baaf27834eb5f75c75ba1ddb0054e72a1cf88a52d685601acd4781fafec025",
231
267
  "status": "verified",
232
268
  "claims": [
233
269
  {
@@ -243,7 +279,7 @@
243
279
  ]
244
280
  },
245
281
  "funifier-create-level": {
246
- "configHash": "c50ed12fceedfc2288bd2006f743528f440ab57de9f8321ae0297f0eb6199eae",
282
+ "configHash": "807c32381d542e5e1bbd678b29bc79751af340b7e00a31264a1dd24839306dab",
247
283
  "status": "verified",
248
284
  "claims": [
249
285
  {
@@ -269,7 +305,7 @@
269
305
  ]
270
306
  },
271
307
  "funifier-create-lottery": {
272
- "configHash": "073bed793dd691ac19d095fa52839f4129532b7ff49eef4e35e9c84954d1b540",
308
+ "configHash": "ba9945f649f2e2fe91d545d27131b0e705b729deba192d121b8e41a03e3d5078",
273
309
  "status": "verified",
274
310
  "claims": [
275
311
  {
@@ -295,7 +331,7 @@
295
331
  ]
296
332
  },
297
333
  "funifier-create-mystery": {
298
- "configHash": "ab423bcca3614806f63a3366db19629fe3127dc2989975f181cb0171be37d312",
334
+ "configHash": "0251df7c50e9629c592abef26894cc4b37d2a00c32ce6021c47eeedad3df35bb",
299
335
  "status": "verified",
300
336
  "claims": [
301
337
  {
@@ -311,7 +347,7 @@
311
347
  ]
312
348
  },
313
349
  "funifier-create-notification": {
314
- "configHash": "1fd4dbbc94ba9aa72ae464b4c2c40b23f612e8b7c49fae7a8f1136950c0474a2",
350
+ "configHash": "7c3a31fce953b96fd477ddd1f437cc7b617d38b999fec45944a80ab58693306f",
315
351
  "status": "verified",
316
352
  "claims": [
317
353
  {
@@ -327,7 +363,7 @@
327
363
  ]
328
364
  },
329
365
  "funifier-create-point": {
330
- "configHash": "68c1da1a809c5ad6a360766d257bdc14fefff7162e4072ab2221e0f13d0be0a1",
366
+ "configHash": "a5d84d1e85e7fe8b3d6af3acb19122243c75456c101b991491ee510716607c4a",
331
367
  "status": "verified",
332
368
  "claims": [
333
369
  {
@@ -353,7 +389,7 @@
353
389
  ]
354
390
  },
355
391
  "funifier-create-quiz": {
356
- "configHash": "ccec76679412c2f2a45b3afd30cc0cf266ed29dc1d71c23c6542638819556c7a",
392
+ "configHash": "dd932897848f30800c71e17acdaf08699dd1c28f5d5b9de618c696bfa88be5b6",
357
393
  "status": "verified",
358
394
  "claims": [
359
395
  {
@@ -379,7 +415,7 @@
379
415
  ]
380
416
  },
381
417
  "funifier-create-scheduler": {
382
- "configHash": "841ffa14cc86b91dd2f1d744bbc55def6bfe6de6baf6380fff435625963fdd7e",
418
+ "configHash": "9f986517a5c5d8caf315b6ee5ce93150666a7f3a0bf45aefe4f46fa883665e38",
383
419
  "status": "verified",
384
420
  "claims": [
385
421
  {
@@ -395,12 +431,12 @@
395
431
  ]
396
432
  },
397
433
  "funifier-create-story": {
398
- "configHash": "df98fbfb6f812c49736fb99ab6d6de2575289ce28e915bc95ec994158e3bc9b5",
434
+ "configHash": "5c53e20e201766c7b2fe29a253c50655cd7fad0fc82edb3543957c79697996bf",
399
435
  "status": "verified",
400
436
  "claims": []
401
437
  },
402
438
  "funifier-create-swap": {
403
- "configHash": "ef4c5ca6bb0d66976d97dc8a2516da8f112ae0eb6a4e449d678f151dfed646e8",
439
+ "configHash": "3067da9da8726b0f0191aac05d711e2870dada98044e9fd442bdb2472654d053",
404
440
  "status": "verified",
405
441
  "claims": [
406
442
  {
@@ -416,7 +452,7 @@
416
452
  ]
417
453
  },
418
454
  "funifier-create-trigger": {
419
- "configHash": "44204bb97039cb17d5a1d97757562d9fa16baefd5caf0e1f1a6cc0fa0390db4d",
455
+ "configHash": "21a46732f80324f9662064e8d96528f26c7cfa277f152905fd537be8ef12dab1",
420
456
  "status": "verified",
421
457
  "claims": [
422
458
  {
@@ -452,7 +488,7 @@
452
488
  ]
453
489
  },
454
490
  "funifier-create-virtual-good": {
455
- "configHash": "70415901417a63692f7a1325300d2b57644a067dc55917a9e9b64e92bd3c14af",
491
+ "configHash": "9a78b550bfdd28271fcb1d225da8a6f0a68112735209765b32c34fe0be981c33",
456
492
  "status": "verified",
457
493
  "claims": [
458
494
  {
@@ -468,7 +504,7 @@
468
504
  ]
469
505
  },
470
506
  "funifier-create-webhook": {
471
- "configHash": "e9ff0ebe0b9829bf61011b3abc846806a36f0245e4075a34aecdcb86d837c081",
507
+ "configHash": "cc84a72a02ade14142576f6f66a806e10b400ce624b42509dd9bcb8777424d70",
472
508
  "status": "verified",
473
509
  "claims": [
474
510
  {
@@ -484,7 +520,7 @@
484
520
  ]
485
521
  },
486
522
  "funifier-create-websocket": {
487
- "configHash": "66965c501a68223a84647f0fd2f5bb81fb898318e86e901819a7e9a560201096",
523
+ "configHash": "0689d726bebbe170d03132e53a943978b78392e2c1240a38b147e3813d3f6322",
488
524
  "status": "verified",
489
525
  "claims": [
490
526
  {
@@ -500,7 +536,7 @@
500
536
  ]
501
537
  },
502
538
  "funifier-create-widget": {
503
- "configHash": "ed11b1648c6a67e24aa4e6a35a29864a48fa8968b7c5e83275cde8f2464273f2",
539
+ "configHash": "d4e6e29b2dadbe4838b1dd1e820989c2fc88c7a63fc41e0b871ec725cf2c3296",
504
540
  "status": "verified",
505
541
  "claims": [
506
542
  {
@@ -516,12 +552,12 @@
516
552
  ]
517
553
  },
518
554
  "funifier-date-handling": {
519
- "configHash": "82a41d6369f0a1e2ad5e0a09d7c6c318c970436c7b59a0b42611cd6d5e14575e",
555
+ "configHash": "07e910af002f3bbf6cee50fedf10acfea9a40162eeee567256076bdf612b02e5",
520
556
  "status": "verified",
521
557
  "claims": []
522
558
  },
523
559
  "funifier-debug": {
524
- "configHash": "37c75988e8ddf1c5eb9ef0593a83159410d85a744eb1ff262ba02dc4789fa2a4",
560
+ "configHash": "d464a6ec61f5122298bd6d855a1584be307d358b4253f4854643aeaffff319e9",
525
561
  "status": "verified",
526
562
  "claims": [
527
563
  {
@@ -537,7 +573,7 @@
537
573
  ]
538
574
  },
539
575
  "funifier-help": {
540
- "configHash": "7af4ce568c8429bd33b797d070dfaed47ad08618ce1074c8532df0aa33fed1c8",
576
+ "configHash": "1da15e3fcabe8f807cab51fcfbba47686a1a4c03407b6808f5364c314f963908",
541
577
  "status": "verified",
542
578
  "claims": [
543
579
  {
@@ -553,7 +589,7 @@
553
589
  ]
554
590
  },
555
591
  "funifier-implement-frontend": {
556
- "configHash": "cb9700aa8484384f913ef6871ea2a40652c08e394b9e1bfb111706da094c56b5",
592
+ "configHash": "cab5471ae97ab4413b4798b6256f388457246cf91eb494d3d58c722cb806e2ff",
557
593
  "status": "verified",
558
594
  "claims": [
559
595
  {
@@ -579,17 +615,17 @@
579
615
  ]
580
616
  },
581
617
  "funifier-import-csv": {
582
- "configHash": "24ca1b985f50466dd781e3cb1b9a67f152b748d60735b8e8211b305a4c74b261",
618
+ "configHash": "9467463bbae7c65363df7137243ff034ccc23dba2426f3e05211ac5bde03ed17",
583
619
  "status": "verified",
584
620
  "claims": []
585
621
  },
586
622
  "funifier-manage-indexes": {
587
- "configHash": "d4097ecfb403b1df6ea5e2adaf6359b2e663d3ed57f944476abbd9d77debbb1c",
623
+ "configHash": "bebb7f00e3b8a6eff8276b7264a33dfc6c912a58adc96f3fc8406e8cb1675df3",
588
624
  "status": "verified",
589
625
  "claims": []
590
626
  },
591
627
  "funifier-manage-player": {
592
- "configHash": "77efa151e25e2672fcea590f4a5f6bf2f77745875a297e79351f544cff04b73f",
628
+ "configHash": "9d4a397fac4cd79d2a6168ef689ecd8dff76a28324f9a3e67492a1db573d127f",
593
629
  "status": "verified",
594
630
  "claims": [
595
631
  {
@@ -615,7 +651,7 @@
615
651
  ]
616
652
  },
617
653
  "funifier-manage-team": {
618
- "configHash": "624ed9b06b109a2a07e4a921537df1f31d91125486c8ab26745a913c39ac1876",
654
+ "configHash": "63c9ead124e4ffbab71fea8d320e12edc9aa48053bd9607c315bdd350fd1e5ac",
619
655
  "status": "verified",
620
656
  "claims": [
621
657
  {
@@ -631,7 +667,7 @@
631
667
  ]
632
668
  },
633
669
  "funifier-upload-file": {
634
- "configHash": "93b0c2d388aa465794cedd461006dcc1f93e426001c710cb90cc595d076a5ea1",
670
+ "configHash": "9c3dcaf7351189c3395ae3ea5ac122d5b470b3e1db0b0fd94384880c0094f19c",
635
671
  "status": "verified",
636
672
  "claims": []
637
673
  }
@@ -0,0 +1,229 @@
1
+ # Permission Audit Guide
2
+
3
+ ## 1. Visão Geral
4
+
5
+ ### 1.1 O que é este documento
6
+
7
+ Este guia descreve como usar o `funifier_permissions action=audit` para verificar deterministicamente se os escopos salvos no documento de segurança cobrem os endpoints que o seu projeto usa — e se há escopos concedidos que nenhum endpoint requer. O motor de auditoria espelha `SecurityFilter.doFilter` de `funifier-service`; o veredicto é reproduzível e unit-testado, não dependente de raciocínio LLM.
8
+
9
+ ### 1.2 Quando consultar
10
+
11
+ - Antes de alterar escopos: saber o que cada role/app autoriza hoje.
12
+ - Após adicionar endpoints: verificar se o scope cobre as novas chamadas.
13
+ - Quando ocorrem 401 inesperados: mapear exatamente qual token está faltando.
14
+ - Ao auditar mínimo privilégio: identificar tokens excedentes que podem ser removidos.
15
+
16
+ ### 1.3 Quando NÃO consultar
17
+
18
+ - Para **alterar** escopos — use `save_security_role` / `save_api_app` (ações separadas, requerem confirmação do utilizador).
19
+ - Para **perguntas sobre Studio roles/assignments** (permissões de interface) — o audit cobre só API scopes (documento `security`).
20
+ - Para **verificar backups** — use `verify_backup`.
21
+
22
+ ### 1.4 Índice de decisão
23
+
24
+ | Problema / Situação | O que fazer | Seção |
25
+ |---|---|---|
26
+ | Saber qual auth context usar para uma chamada | Tabela de autenticação | §2 |
27
+ | Construir o manifest de chamadas do projeto | Formato do manifest + exemplo | §3 |
28
+ | Interpretar `missing` / `excess` / `danger` / `missing-principal` | Tipos de finding | §4 |
29
+ | Entender como o motor avalia `(method, path, scope)` | Gramática de escopo | §5 |
30
+ | Saber o que fazer com os findings | Regra de read-only + remediação | §6 |
31
+
32
+ ### 1.5 Restrições críticas
33
+
34
+ > ⚠️ **O audit é estritamente read-only.** Nenhuma chamada de escrita é feita; nenhum snapshot é gravado. Findings são candidatos para revisão — nunca são aplicados automaticamente.
35
+
36
+ > ⚠️ **Bearer tokens embtem o scope no momento do login.** Uma alteração de role só afeta sessões novas. Se você corrigiu o scope mas o jogador ainda recebe 401, peça-lhe que faça login novamente.
37
+
38
+ > ⚠️ **O manifest cobre um repo de cada vez.** Se a sua aplicação tem múltiplos repositórios a chamar a mesma gamificação, combine as entradas manualmente no manifest.
39
+
40
+ ---
41
+
42
+ ## 2. Tabela de Auth Context
43
+
44
+ Cada chamada API usa um mecanismo de autenticação diferente. O motor resolve qual scope se aplica com base nesse mecanismo (`SecurityFilter.java:153-211`):
45
+
46
+ | `auth` no manifest | Mecanismo HTTP | Scope usado |
47
+ |--------------------|----------------|-------------|
48
+ | `"public"` | `Basic base64(API_KEY:)` — sem secret | Scope da role `public` no documento de segurança |
49
+ | `"player"` | `Bearer <player token>` | Scope da role sob a qual o jogador fez login (normalmente `player`) — **embutido no token no momento do login** |
50
+ | `"app:<name>"` | `Basic base64(API_KEY:APP_SECRET)` | Scope da entrada `apps[]` cujo `name` é `<name>` |
51
+ | `"role:<name>"` | Qualquer Bearer com role customizada | Scope da role `<name>` no documento de segurança |
52
+
53
+ **Heurísticas de classificação por stack:**
54
+
55
+ - Chamadas sem cabeçalho `Authorization` (ou com API_KEY sem secret) → `"public"`
56
+ - Chamadas com token JWT player (`Authorization: Bearer ...`) → `"player"` (ou o nome da role customizada se o SDK permitir escolher)
57
+ - Chamadas com app_secret embutido em env var / config → `"app:<name>"` (use o `name` do entry em `apps[]`)
58
+ - Chamadas de servidor para servidor com secret fixo → `"app:<name>"`
59
+
60
+ ---
61
+
62
+ ## 3. Formato do Manifest
63
+
64
+ ### 3.1 Schema
65
+
66
+ ```json
67
+ {
68
+ "version": 1,
69
+ "entries": [
70
+ {
71
+ "method": "GET | POST | PUT | DELETE",
72
+ "path": "/v3/...",
73
+ "auth": "<auth context — ver §2>",
74
+ "evidence": "<file:line ou descrição>",
75
+ "confidence": "high | low"
76
+ }
77
+ ]
78
+ }
79
+ ```
80
+
81
+ - **`version`**: deve ser `1`.
82
+ - **`entries`**: máximo de 500 entradas. Cada entrada representa uma chamada distinta de `(method, path, auth)`.
83
+ - **`method`**: case-insensitive; normalizado para maiúsculas internamente.
84
+ - **`path`**: o prefixo `/v3` é adicionado automaticamente se ausente.
85
+ - **`auth`**: obrigatório. Sem `auth` o motor não sabe qual scope avaliar.
86
+ - **`evidence`**: obrigatório. `"src/api.ts:42"` ou `"chamada descoberta em SDK wrapper"`.
87
+ - **`confidence`**: `"high"` (default) para chamadas confirmadas por código; `"low"` para URLs dinâmicas ou inferidas.
88
+
89
+ ### 3.2 Exemplo
90
+
91
+ ```json
92
+ {
93
+ "version": 1,
94
+ "entries": [
95
+ { "method": "GET", "path": "/v3/player/me", "auth": "player", "evidence": "src/profile.ts:12", "confidence": "high" },
96
+ { "method": "POST", "path": "/v3/action/log", "auth": "player", "evidence": "src/events.ts:58", "confidence": "high" },
97
+ { "method": "GET", "path": "/v3/widget/rankings", "auth": "public", "evidence": "src/embed.html:1" },
98
+ { "method": "POST", "path": "/v3/player", "auth": "app:backend", "evidence": "workers/sync.ts:34", "confidence": "high" },
99
+ { "method": "GET", "path": "/v3/database/orders", "auth": "app:backend", "evidence": "workers/sync.ts:80", "confidence": "high" },
100
+ { "method": "GET", "path": "/v3/challenge", "auth": "player", "evidence": "src/gamification.ts:7" }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ ### 3.3 Chamadas com URL dinâmica ou cliente wrapper
106
+
107
+ Quando não é possível determinar o path exato estaticamente:
108
+ - Use o prefixo mais específico que conhece (e.g. `/v3/database/orders` em vez de só `/v3/database`).
109
+ - Defina `"confidence": "low"` para sinalizar incerteza.
110
+ - **Nunca omita** uma chamada suspeita — é melhor um finding de baixa confiança do que um 401 não detectado em produção.
111
+
112
+ ---
113
+
114
+ ## 4. Tipos de Finding
115
+
116
+ | Severity | Significado | Impacto |
117
+ |----------|-------------|---------|
118
+ | `missing` | A chamada seria negada com o scope atual | Quebra funcionalidade — 401 em produção |
119
+ | `excess` | Token concedido mas nenhuma entrada do manifest o requer | Candidato a remoção (mínimo privilégio) |
120
+ | `danger` | Padrão de risco estático (independente do manifest) | Risco de segurança — revisar imediatamente |
121
+ | `manual-review` | Token não-path (`cross_domain`, `write_upload`, etc.) — não verificável por análise de caminhos | Verificar manualmente |
122
+ | `public-no-scope-needed` | Path público — scope não é verificado pelo servidor | Informativo; não entra no cálculo de missing/excess |
123
+ | `missing-principal` | Auth context referenciado no manifest não existe no documento de segurança | Entradas desse auth não foram avaliadas |
124
+
125
+ ### 4.1 Findings de `danger` estáticos
126
+
127
+ Estes são verificados independentemente do manifest:
128
+
129
+ - `write_all` / `delete_all` / `database` na role `public` → unauthenticated callers têm acesso privilegiado
130
+ - `read_encrypted_field_values` / `read_encrypted_player_password` na role `public` ou `player` → dados encriptados expostos
131
+ - App com `scope` vazio → nenhuma chamada será autorizada para esse app
132
+
133
+ ### 4.2 Narrowing para tokens broad
134
+
135
+ Quando `read_all`, `write_all` ou `delete_all` autoriza as suas entradas mas tokens exatos (`read_player`, `write_game`, etc.) cobririam o mesmo uso, o finding inclui `narrowingSuggestion` com os tokens exatos sugeridos. Verifique se há consumidores fora deste manifest antes de remover o token broad.
136
+
137
+ ---
138
+
139
+ ## 5. Gramática de Escopo (SecurityFilter.java)
140
+
141
+ O motor espelha exatamente `SecurityFilter.doFilter`. Ordem de avaliação para cada `(method, path)`:
142
+
143
+ ### 5.1 Paths públicos (sem scope)
144
+
145
+ `GET /v3/widget`, `GET /v3/global`, `GET /v3/system/global` e seus sub-paths passam sem verificação de scope (`SecurityFilter.java:108-151`). Entradas que acertam estes paths são classificadas `public-no-scope-needed`.
146
+
147
+ ### 5.2 Contexto de autenticação → scope
148
+
149
+ O scope avaliado depende do mecanismo de auth (ver §2). Studio/Account tokens não passam pela gramática de scope — estão fora do escopo do audit.
150
+
151
+ ### 5.3 Exceções especiais de POST
152
+
153
+ Verificadas antes da gramática (`SecurityFilter.java:259-263`):
154
+
155
+ - `POST /v3/action/log` → autorizado se o scope contiver `write_actionlog` (sem precisar de `write_all` ou `write_action_log`)
156
+ - `POST /v3/mobile/device` → autorizado com qualquer scope não-vazio
157
+
158
+ ### 5.4 Regras gramaticais
159
+
160
+ Para `(method, path)` não cobertos acima (`SecurityFilter.java:230-253`):
161
+
162
+ 1. **verb** = `GET`→`read`, `POST`/`PUT`→`write`, `DELETE`→`delete`
163
+ 2. **entity_full** = path após `/v3/`, com `/` substituído por `_` (e.g. `/v3/database/orders/aggregate` → `database_orders_aggregate`)
164
+ 3. Verifica na ordem:
165
+ - `{verb}_all` → rule `all`
166
+ - `{verb}_{entity_full}` → rule `exact`
167
+ - `{verb}_{prefix}_all` (minus-1 trailing segment) → rule `fallback-m1`
168
+ - `{verb}_{prefix}_all` (minus-2 trailing segments) → rule `fallback-m2`
169
+ - Se nenhum → denied
170
+
171
+ > **O motor para em m2.** Não há fallback m3 ou superior, mesmo que o path tenha mais segmentos.
172
+
173
+ ### 5.5 Keyword `database` (DatabaseRest.java)
174
+
175
+ **Regra crítica:** qualquer path `/v3/database/...` requer o token literal `database` no scope, **além** do token gramatical. Aplica-se a **todos os verbos incluindo GET**. Os 13 handlers de `DatabaseRest.java` verificam este keyword explicitamente.
176
+
177
+ Exemplo: para autorizar `GET /v3/database/orders`, o scope precisa de `read_database_orders` (ou `read_all`) **E** `database`.
178
+
179
+ ### 5.6 Normalização do scope
180
+
181
+ Espaços são removidos e tokens são separados por vírgula antes da avaliação. `"read_all, write_all"` equivale a `"read_all,write_all"`.
182
+
183
+ ---
184
+
185
+ ## 6. Read-Only Rule e Remediação
186
+
187
+ ### 6.1 O audit nunca escreve
188
+
189
+ O `funifier_permissions action=audit` é a única ação deste tool que não requer conexão para findings de `danger` (ainda requer para carregar o documento de segurança). Não faz mutações, não grava snapshots.
190
+
191
+ ### 6.2 Fluxo de remediação
192
+
193
+ Para cada finding `missing`:
194
+ 1. Identifique o token exato sugerido em `requiredTokens`.
195
+ 2. Decida se vai adicionar ao scope da role (`save_security_role`) ou do app (`save_api_app`).
196
+ 3. Confirme a intenção com o utilizador antes de executar.
197
+ 4. O tool escreverá um backup automático antes de qualquer mutação.
198
+
199
+ Para cada finding `excess`:
200
+ 1. Verifique se o token pode ser necessário a consumidores **fora** deste manifest.
201
+ 2. Se seguro, remova com `save_security_role` / `save_api_app`.
202
+ 3. Após remover, corra o audit novamente para confirmar que não surgiram novos `missing`.
203
+
204
+ Para findings `danger`:
205
+ 1. Avalie o risco: quem está a usar o contexto público/player afetado?
206
+ 2. Remova o token perigoso ou restrinja o role conforme necessário.
207
+ 3. Para `read_encrypted_*` em `public`: é quase sempre um erro de configuração.
208
+
209
+ ---
210
+
211
+ ## 7. Invocação
212
+
213
+ ```
214
+ funifier_permissions action=audit data=<manifest JSON>
215
+ ```
216
+
217
+ O manifest é passado como string JSON no campo `data`. Exemplo de chamada:
218
+
219
+ ```json
220
+ {
221
+ "action": "audit",
222
+ "data": "{\"version\":1,\"entries\":[{\"method\":\"GET\",\"path\":\"/v3/player/me\",\"auth\":\"player\",\"evidence\":\"src/api.ts:10\"}]}"
223
+ }
224
+ ```
225
+
226
+ O resultado inclui:
227
+ - `manifest_entries`: número de entradas avaliadas
228
+ - `findings`: lista de findings com `severity`, `principal`, `rule`, `evidence`, `requiredTokens`, `excessTokens`, `narrowingSuggestion`, `detail`
229
+ - `notes`: notas de completude, Bearer freshness, e instrução de verificação para excess
@@ -106,6 +106,7 @@ A Funifier funciona como um **backend de aplicações**. Independentemente do ti
106
106
  | Java Managers | `guides/java-managers.md` | Referência de managers Java | Métodos disponíveis em cada manager (PlayerManager, ActionManager, etc.) para uso em triggers, schedulers e public endpoints |
107
107
  | Java Entities | `guides/java-entities.md` | Referência de entidades Java | Campos e tipos dos objetos (Player, Achievement, ActionLog, etc.) e mapeamento para coleções MongoDB |
108
108
  | Java Libraries | `guides/java-libraries.md` | Bibliotecas e utilitários Java | JsonUtil, Guid, DateUtil, Unirest, EmailBuilder, Jongo — métodos e exemplos de uso |
109
+ | Permission Audit | `guides/permission-audit.md` | Auditoria de permissões de API | Cross-check código↔Funifier: manifest de chamadas → missing scopes, excess tokens, danger findings; gramática de scope (SecurityFilter), keyword `database`, auth context, remediação |
109
110
 
110
111
  ---
111
112