An authenticated publish-service reader can invoke /api/av/removeUnusedAttributeView and cause persistent deletion of arbitrary attribute view (AV) definition files from the workspace.
The route is protected only by generic CheckAuth, which accepts publish RoleReader requests. The handler forwards a caller-controlled id directly into a model function that deletes data/storage/av/<id>.json without verifying either:
This is a persistent integrity and availability issue reachable from the publish surface.
RoleReader JWTClaimsKeyRole: RoleReader,
CheckAuth accepts RoleReaderif role := GetGinContextRole(c); IsValidRole(role, []Role{
RoleAdministrator,
RoleEditor,
RoleReader,
}) {
c.Next()
return
}
CheckAuth onlyginServer.Handle("POST", "/api/av/removeUnusedAttributeView", model.CheckAuth, removeUnusedAttributeView)
There is no CheckAdminRole and no CheckReadonly.
id directly to the delete sinkavID := arg["id"].(string)
model.RemoveUnusedAttributeView(avID)
func RemoveUnusedAttributeView(id string) {
absPath := filepath.Join(util.DataDir, "storage", "av", id+".json")
if !filelock.IsExist(absPath) {
return
}
...
if err = filelock.RemoveWithoutFatal(absPath); err != nil {
...
return
}
IncSync()
}
Crucially, this function does not verify that the supplied AV is actually unused. The name of the function suggests a cleanup helper, but the implementation is really "delete AV file by id if it exists".
avIDavIDavID is not secret. It is exposed extensively in frontend markup as data-av-id.
Examples:
Any publish-visible database/attribute view can therefore disclose a valid avID to the attacker.
data-av-id value from the page/DOM./api/av/removeUnusedAttributeView through the publish service.RoleReader token.CheckAuth accepts the request.avID to model.RemoveUnusedAttributeView.data/storage/av/<avID>.json.Request:
POST /api/av/removeUnusedAttributeView HTTP/1.1
Host: <publish-host>:<publish-port>
Content-Type: application/json
Authorization: Basic <publish-account-creds-if-enabled>
{
"id": "<exposed-data-av-id>"
}
Expected result:
data/storage/av/This gives a low-privileged publish reader a destructive persistent write primitive against workspace data.
Practical consequences include:
The bug affects integrity and availability, not merely UI state.
At minimum:
Safe router fix:
ginServer.Handle("POST", "/api/av/removeUnusedAttributeView",
model.CheckAuth,
model.CheckAdminRole,
model.CheckReadonly,
removeUnusedAttributeView,
)
And inside the model or handler, reject deletion unless the target id is present in UnusedAttributeViews(...).
{
"github_reviewed_at": "2026-04-10T19:32:07Z",
"github_reviewed": true,
"severity": "HIGH",
"nvd_published_at": "2026-04-16T23:16:33Z",
"cwe_ids": [
"CWE-285"
]
}