system-collections-refused.json 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. {
  2. "name": "verify-system-collections-refused",
  3. "comment": "A workflow gets nowhere near the collections SmartBotic runs on, even with defaultAccess read-write. This is the setting that matters: a blanket yes to every unlisted name. Reading projects exposes who may do what; writing it is a grant of access somebody gave themselves. Writing workflows edits another workflow's graph without passing a single permission check.\n\nThis was open. The runner and the webserver each kept their own list of protected collections and the two had drifted - the runner named five, the webserver ten, and \"projects\" was in neither. The list is one list now, in storage/system_collections.hpp, so a name added there is protected in both places at once.\n\nThe control is an ordinary collection written under the same defaultAccess, which must still succeed: this test is about protecting the system's own data, not about turning defaultAccess off.",
  4. "settings": {
  5. "storagePermissions": {
  6. "defaultAccess": "read-write"
  7. }
  8. },
  9. "nodes": [
  10. {
  11. "id": "n1",
  12. "name": "Trigger",
  13. "type": "click-trigger",
  14. "position": { "x": 0, "y": 0 },
  15. "config": {}
  16. },
  17. {
  18. "id": "projects",
  19. "name": "Read who may do what",
  20. "type": "storage-query",
  21. "position": { "x": 0, "y": 100 },
  22. "config": { "collectionSource": "manual", "collectionManual": "projects" }
  23. },
  24. {
  25. "id": "workflows",
  26. "name": "Read every workflow",
  27. "type": "storage-query",
  28. "position": { "x": 0, "y": 200 },
  29. "config": { "collectionSource": "manual", "collectionManual": "workflows" }
  30. },
  31. {
  32. "id": "users",
  33. "name": "Read the password hashes",
  34. "type": "storage-query",
  35. "position": { "x": 0, "y": 300 },
  36. "config": { "collectionSource": "manual", "collectionManual": "users" }
  37. },
  38. {
  39. "id": "escalate",
  40. "name": "Grant itself a project role",
  41. "type": "storage-insert",
  42. "position": { "x": 0, "y": 400 },
  43. "config": {
  44. "collectionSource": "manual",
  45. "collectionManual": "projects",
  46. "documentData": "{\"name\": \"trespass\"}"
  47. }
  48. },
  49. {
  50. "id": "control",
  51. "name": "An ordinary collection still works",
  52. "type": "storage-insert",
  53. "position": { "x": 0, "y": 500 },
  54. "config": {
  55. "collectionSource": "manual",
  56. "collectionManual": "wfprobe_control_collection",
  57. "documentData": "{\"probe\": \"control\"}"
  58. }
  59. }
  60. ],
  61. "connections": [
  62. { "sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "projects", "targetInput": "data" },
  63. { "sourceNodeId": "projects", "sourceOutput": "main", "targetNodeId": "workflows", "targetInput": "data" },
  64. { "sourceNodeId": "workflows", "sourceOutput": "main", "targetNodeId": "users", "targetInput": "data" },
  65. { "sourceNodeId": "users", "sourceOutput": "main", "targetNodeId": "escalate", "targetInput": "data" },
  66. { "sourceNodeId": "escalate", "sourceOutput": "main", "targetNodeId": "control", "targetInput": "data" }
  67. ],
  68. "expect": {
  69. "projects": {
  70. "status": "completed",
  71. "output": { "error": "No read access to collection: projects" }
  72. },
  73. "workflows": {
  74. "status": "completed",
  75. "output": { "error": "No read access to collection: workflows" }
  76. },
  77. "users": {
  78. "status": "completed",
  79. "output": { "error": "No read access to collection: users" }
  80. },
  81. "escalate": {
  82. "status": "completed",
  83. "output": { "success": false, "error": "No write access to collection: projects" }
  84. },
  85. "control": {
  86. "status": "completed",
  87. "output": { "success": true }
  88. }
  89. }
  90. }