fix: address critical security and data integrity issues from cross-audit
- Add expire_time > NOW() filter to findActive() preventing stale subscriptions - Add express-rate-limit on login endpoints (user: 10/15min, admin: 5/15min) - Add production guard for default admin credentials - Fix BLE bindDevice userId encoding (uint32 instead of hexToBytes on numeric) - Wrap adminCreate in transaction to prevent race condition - Add settings cache invalidation after admin saves - Read trial_days from settings instead of hardcoding 7 - Fix double JSON.stringify in commandDao.finish call - Cancel stale pending bindings before creating new ones - Reduce token refresh grace period from 3 days to 1 day - Fix subscribe-success to fetch expiry from server (correct for renewals) - Add keep-alive name property to DashboardView and SettingsView - Fix BLE disconnect() to preserve listener registrations across reconnects
这个提交包含在:
@@ -1,3 +1,5 @@
|
||||
var api = require('../../utils/api')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
@@ -14,21 +16,32 @@ Page({
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
var self = this
|
||||
var app = getApp()
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
self.setData({ statusBarHeight: app.globalData.statusBarHeight })
|
||||
|
||||
var planMap = { yearly: '年卡会员', monthly: '月卡会员', trial: '试用会员' }
|
||||
var durationMap = { yearly: 365, monthly: 30, trial: 7 }
|
||||
var plan = options.plan || 'yearly'
|
||||
self.setData({ planName: planMap[plan] || '会员' })
|
||||
|
||||
var now = new Date()
|
||||
now.setDate(now.getDate() + (durationMap[plan] || 365))
|
||||
var y = now.getFullYear()
|
||||
var m = ('0' + (now.getMonth() + 1)).slice(-2)
|
||||
var d = ('0' + now.getDate()).slice(-2)
|
||||
this.setData({
|
||||
planName: planMap[plan] || '会员',
|
||||
expiryDate: y + '年' + m + '月' + d + '日'
|
||||
// Fetch actual subscription expiry from server instead of computing client-side
|
||||
api.getSubscription().then(function (res) {
|
||||
if (res && res.expire_time) {
|
||||
var date = new Date(res.expire_time)
|
||||
var y = date.getFullYear()
|
||||
var m = ('0' + (date.getMonth() + 1)).slice(-2)
|
||||
var d = ('0' + date.getDate()).slice(-2)
|
||||
self.setData({ expiryDate: y + '年' + m + '月' + d + '日' })
|
||||
}
|
||||
}).catch(function () {
|
||||
// Fallback: compute from current date if server call fails
|
||||
var durationMap = { yearly: 365, monthly: 30, trial: 7 }
|
||||
var now = new Date()
|
||||
now.setDate(now.getDate() + (durationMap[plan] || 365))
|
||||
var y = now.getFullYear()
|
||||
var m = ('0' + (now.getMonth() + 1)).slice(-2)
|
||||
var d = ('0' + now.getDate()).slice(-2)
|
||||
self.setData({ expiryDate: y + '年' + m + '月' + d + '日' })
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ function queryStatus() {
|
||||
}
|
||||
|
||||
function bindDevice(userId, bindToken) {
|
||||
var userBytes = protocol.hexToBytes(userId)
|
||||
var userBytes = protocol.uint32ToBytes(parseInt(userId, 10))
|
||||
var tokenBytes = protocol.hexToBytes(bindToken)
|
||||
var ts = Math.floor(Date.now() / 1000)
|
||||
var tsBytes = protocol.uint32ToBytes(ts)
|
||||
@@ -165,7 +165,7 @@ function bindDevice(userId, bindToken) {
|
||||
}
|
||||
|
||||
function unbindDevice(userId) {
|
||||
var userBytes = protocol.hexToBytes(userId)
|
||||
var userBytes = protocol.uint32ToBytes(parseInt(userId, 10))
|
||||
var payload = [0x02].concat(userBytes)
|
||||
return writeCommandWithRetry(protocol.CMD.UNBIND, payload)
|
||||
}
|
||||
|
||||
@@ -236,7 +236,9 @@ function disconnect() {
|
||||
_chars = {}
|
||||
var commands = require('./commands')
|
||||
commands.clearPendingAcks()
|
||||
_listeners = {}
|
||||
// Only emit disconnected event; do not clear _listeners so that
|
||||
// subscribers (other modules) retain their registrations across reconnects.
|
||||
emit('disconnect_cleanup', null)
|
||||
wx.closeBluetoothAdapter({})
|
||||
}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户