we had this report from a user:
Hello Giveth security team,
I believe I found a service vulnerability in the Giveth frontend/backend project update flow. I did not execute this against production data. The evidence below comes from source review plus read-only public GraphQL queries.
Reporter: davethediver
Email: dave.the.diver1010@gmail.com
Ethereum address for reward distribution: 0xa882D8296CCd4eD5A4Ffe4cFaDd1bDf8DFA541D6
Summary
A project owner can store arbitrary HTML in a project update through addProjectUpdate / editProjectUpdate. If that update is selected as a featured project update, the homepage featured update card renders content with dangerouslySetInnerHTML without sanitization.
This creates a stored XSS path for visitors viewing the homepage/featured project update card. A particularly risky path is:
- A project owner creates a benign project update.
- Giveth admin features that project update via AdminJS.
- The project owner later edits the same featured update to include malicious HTML/event attributes.
- The homepage fetches the featured update and injects
content directly into the DOM.
Impact
JavaScript execution in the giveth.io origin could read same-origin localStorage values, including the app's auth token storage keys, and perform authenticated GraphQL/API actions as the victim. It could also alter donation/project UI, show wallet-phishing prompts, or redirect users from the trusted Giveth origin.
Evidence
Frontend repo: Giveth/giveth-dapps-v2, local HEAD 4cb06d1
The homepage featured update card directly injects raw update HTML:
src/components/views/homepage/projectUpdatesBlock/ProjectUpdateSlide.tsx
<UpdateDesc
dangerouslySetInnerHTML={{
__html: update?.content || '',
}}
/>
That content comes from the public featuredProjectUpdate(projectId) query:
src/apollo/gql/gqlProjects.ts
query featuredProjectUpdate($projectId: Int!) {
featuredProjectUpdate(projectId: $projectId) {
id
title
projectId
userId
content
isMain
createdAt
}
}
Backend repo: Giveth/impact-graph, local HEAD 870dcbc
addProjectUpdate stores the caller-supplied content directly:
src/resolvers/projectResolver.ts
const update = ProjectUpdate.create({
userId: user.userId,
projectId: project.id,
content,
title,
createdAt: new Date(),
isMain: false,
});
const save = await ProjectUpdate.save(update);
editProjectUpdate lets the project owner replace content directly:
update.title = title;
update.content = content;
await update.save();
The entity stores content as a plain string column. The insert/update hook only generates a text summary; it does not sanitize the stored HTML:
src/entities/project.ts
@Field(_type => String)
@Column()
content: string;
@BeforeInsert()
setProjectUpdateContentSummary() {
this.contentSummary = getHtmlTextSummary(this.content);
}
AdminJS selects existing update rows as featured records by projectUpdateId:
src/server/adminJs/tabs/projectsTab.ts
const featuredProject = FeaturedUpdate.create({
projectUpdateId: update.id,
projectId: update.project!.id,
});
await featuredProject.save();
The backend query returns the referenced update row:
src/resolvers/projectResolver.ts
const featuredProject = await FeaturedUpdate.createQueryBuilder(
'featuredProject',
)
.innerJoinAndSelect('featuredProject.projectUpdate', 'projectUpdate')
.where('featuredProject.projectId = :projectId', { projectId })
.getOne();
return featuredProject!.projectUpdate;
The backend tests also show that arbitrary HTML is preserved in editProjectUpdate responses rather than normalized/sanitized:
src/resolvers/projectResolver.test.ts
content: '<div>TestProjectUpdateAfterUpdateFateme</div>',
...
assert.equal(
result.data.data.editProjectUpdate.content,
'<div>TestProjectUpdateAfterUpdateFateme</div>',
);
I also confirmed with a read-only public GraphQL query to https://mainnet.serve.giveth.io/graphql that featuredProjectUpdate.content is returned as raw HTML for currently featured projects.
Safe reproduction outline
Please run this only in a local/staging environment:
- Create or seed a project owned by a normal user.
- Add a normal update as that project owner.
- Feature that update via AdminJS so a
FeaturedUpdate row references it.
- As the project owner, call:
mutation EditProjectUpdate($updateId: Float!, $title: String!, $content: String!) {
editProjectUpdate(updateId: $updateId, title: $title, content: $content) {
id
title
content
}
}
With variables similar to:
{
"updateId": 123,
"title": "Update",
"content": "<p>hello</p><img src=x onerror=\"document.body.setAttribute('data-giveth-xss','1')\">"
}
- Visit the homepage/featured projects section where
ProjectUpdateSlide renders that featured update.
- Observe that the DOM mutation from the event handler occurs. A real attacker could replace that harmless marker with token theft, UI manipulation, or same-origin API calls.
Suggested fixes
Sanitize project update HTML on the backend with a strict allowlist before storing or returning it. The allowlist should permit only the Quill tags/attributes Giveth intentionally supports, and should block event handler attributes, javascript: URLs, dangerous SVG/MathML, and unsafe inline style.
Also consider replacing the dangerouslySetInnerHTML use in ProjectUpdateSlide with the same safe rendering path used elsewhere, or render a text summary on the homepage card instead of raw rich HTML.
Because already-featured updates can be edited after admin selection, please also consider revalidating/sanitizing existing project_update.content rows and either snapshotting reviewed featured content or requiring re-review after a featured update is edited.
Thank you
we had this report from a user:
Hello Giveth security team,
I believe I found a service vulnerability in the Giveth frontend/backend project update flow. I did not execute this against production data. The evidence below comes from source review plus read-only public GraphQL queries.
Reporter: davethediver
Email: dave.the.diver1010@gmail.com
Ethereum address for reward distribution: 0xa882D8296CCd4eD5A4Ffe4cFaDd1bDf8DFA541D6
Summary
A project owner can store arbitrary HTML in a project update through
addProjectUpdate/editProjectUpdate. If that update is selected as a featured project update, the homepage featured update card renderscontentwithdangerouslySetInnerHTMLwithout sanitization.This creates a stored XSS path for visitors viewing the homepage/featured project update card. A particularly risky path is:
contentdirectly into the DOM.Impact
JavaScript execution in the
giveth.ioorigin could read same-origin localStorage values, including the app's auth token storage keys, and perform authenticated GraphQL/API actions as the victim. It could also alter donation/project UI, show wallet-phishing prompts, or redirect users from the trusted Giveth origin.Evidence
Frontend repo:
Giveth/giveth-dapps-v2, local HEAD4cb06d1The homepage featured update card directly injects raw update HTML:
src/components/views/homepage/projectUpdatesBlock/ProjectUpdateSlide.tsxThat content comes from the public
featuredProjectUpdate(projectId)query:src/apollo/gql/gqlProjects.tsBackend repo:
Giveth/impact-graph, local HEAD870dcbcaddProjectUpdatestores the caller-suppliedcontentdirectly:src/resolvers/projectResolver.tseditProjectUpdatelets the project owner replacecontentdirectly:The entity stores
contentas a plain string column. The insert/update hook only generates a text summary; it does not sanitize the stored HTML:src/entities/project.tsAdminJS selects existing update rows as featured records by
projectUpdateId:src/server/adminJs/tabs/projectsTab.tsThe backend query returns the referenced update row:
src/resolvers/projectResolver.tsThe backend tests also show that arbitrary HTML is preserved in
editProjectUpdateresponses rather than normalized/sanitized:src/resolvers/projectResolver.test.tsI also confirmed with a read-only public GraphQL query to
https://mainnet.serve.giveth.io/graphqlthatfeaturedProjectUpdate.contentis returned as raw HTML for currently featured projects.Safe reproduction outline
Please run this only in a local/staging environment:
FeaturedUpdaterow references it.With variables similar to:
{ "updateId": 123, "title": "Update", "content": "<p>hello</p><img src=x onerror=\"document.body.setAttribute('data-giveth-xss','1')\">" }ProjectUpdateSliderenders that featured update.Suggested fixes
Sanitize project update HTML on the backend with a strict allowlist before storing or returning it. The allowlist should permit only the Quill tags/attributes Giveth intentionally supports, and should block event handler attributes,
javascript:URLs, dangerous SVG/MathML, and unsafe inline style.Also consider replacing the
dangerouslySetInnerHTMLuse inProjectUpdateSlidewith the same safe rendering path used elsewhere, or render a text summary on the homepage card instead of raw rich HTML.Because already-featured updates can be edited after admin selection, please also consider revalidating/sanitizing existing
project_update.contentrows and either snapshotting reviewed featured content or requiring re-review after a featured update is edited.Thank you