Skip to content

Commit b9d6a67

Browse files
authored
Merge branch 'next' into SER-1991-ss-taskmanager-uses-comment-field-for-identifying-operation
2 parents 0764b43 + 6252a18 commit b9d6a67

File tree

11 files changed

+142
-52
lines changed

11 files changed

+142
-52
lines changed

.github/workflows/main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ name: CI
66
on:
77
# Triggers the workflow on push or pull request events but only for the master branch
88
pull_request:
9-
branches: [ master, next ]
9+
branches: [ master, next, release.24.10 ]
1010

1111
# Allows you to run this workflow manually from the Actions tab
1212
workflow_dispatch:

CHANGELOG.md

+38-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
1+
## Version 24.10.2
2+
Fixes:
3+
- [core] Correct aggregated collection cleanup on event omitting
4+
- [core] Fixed bug where changing passwords results in the loss of the "Global Admin" role
5+
- [core] Fixed bug where exporting incoming data logs could result in "Incorrect parameter \"data\" error
6+
- [core] Removed use of commands which needs admin rights from report manager.
7+
- [crash] Fixed bug in crash ingestion for scenarios where the "app version" is not a string.
8+
- [script] Fixing bug with "delete_old_members" script that led to malformed requests
9+
10+
Enterprise fixes:
11+
- [nps] Fixed bug that showed the wrong nps preview title
12+
113
## Version 24.10.1
2-
Integrating 24.05.16 fixes
14+
Fixes:
15+
- [core] Replaced "Users" with "Sessions" label on technology home widgets
16+
- [push] Improved ability to observe push related errors
17+
- [push] Replaced push plugin with an earlier version of the plugin
18+
19+
Enterprise fixes:
20+
- [cohorts] Fixed issues with nightly cleanup
21+
- [data-manager] Fixed UI bug where rules were not visible when editing "Merge by regex" transformations
22+
- [drill] Fixed wrong pie chart label tooltip in dashboard widget
23+
- [flows] Fixed bug in case of null data in schema
24+
- [license] Fixed bug with MAU type of licenses that would prevent the server from starting
25+
- [nps] Fixed bug in the editor where the "internal name" field was not mandatory
26+
- [nps] Fixed bug where it was possible to submit empty nps surveys
27+
- [ratings] Fixed bug with user consent
28+
- [ratings] Fixed UI bug where "Internal name" was not a mandatory field
29+
30+
Security:
31+
- Bumped cookie-parser from 1.4.6 to 1.4.7
32+
- Bumped express-rate-limit from 7.4.0 to 7.4.1
33+
- Bumped moment-timezone from 0.5.45 to 0.5.46
34+
- Bumped sass from 1.79.3 to 1.79.4
35+
- Fixing minor vulnerability that would allow for unauthorized file upload
36+
37+
Enterprise Features:
38+
- [block] Added a way to filter crashes by their error (stacktrace)
339

440
## Version 24.10
541
Fixes:
@@ -35,39 +71,9 @@ Enterprise Features:
3571
- [users] UI improvements
3672
- [views] Added a quick transition to drill
3773

38-
## Version 24.05.17
39-
Fixes:
40-
- [push] Improved ability to observe push related errors
41-
42-
Enterprise fixes:
43-
- [cohorts] Fixed issues with nightly cleanup
44-
- [data-manager] Fixed UI bug where rules were not visible when editing "Merge by regex" transformations
45-
- [drill] Fixed wrong pie chart label tooltip in dashboard widget
46-
- [flows] Fixed bug in case of null data in schema
47-
- [nps] Fixed bug in the editor where the "internal name" field was not mandatory
48-
- [ratings] Fixed UI bug where "Internal name" was not a mandatory field
49-
50-
Security:
51-
- Fixing minor vulnerability that would allow for unauthorized file upload
52-
53-
## Version 24.05.16
54-
Fixes:
55-
- [core] Replaced "Users" with "Sessions" label on technology home widgets
56-
- [push] Replaced push plugin with an earlier version of the plugin
57-
58-
Enterprise fixes:
59-
- [license] Fixed bug with MAU type of licenses that would prevent the server from starting
60-
- [nps] Fixed bug where it was possible to submit empty nps surveys
61-
- [ratings] Fixed bug with user consent
62-
63-
Security:
64-
- Bumped cookie-parser from 1.4.6 to 1.4.7
65-
- Bumped express-rate-limit from 7.4.0 to 7.4.1
66-
- Bumped moment-timezone from 0.5.45 to 0.5.46
67-
- Bumped sass from 1.79.3 to 1.79.4
68-
6974
## Version 24.05.15
7075
Enterprise fixes:
76+
- [ab-testing] Fixed JSON.parse issue preventing creation of AB tests
7177
- [nps] Fixed UI issues in the widget editor related to the "user consent" section
7278
- [ratings] Fixed rendering issue for escaped values
7379

api/utils/requestProcessor.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,7 @@ const processRequest = (params) => {
11721172
return new Promise(function(resolve) {
11731173
var collectionNameWoPrefix = common.crypto.createHash('sha1').update(obj.key + params.qstring.app_id).digest('hex');
11741174
//removes all document for current segment
1175-
common.db.collection("events" + collectionNameWoPrefix).remove({"s": {$in: obj.list}}, {multi: true}, function(err3) {
1175+
common.db.collection("events_data").remove({"_id": {"$regex": ("^" + params.qstring.app_id + "_" + collectionNameWoPrefix + "_.*")}, "s": {$in: obj.list}}, {multi: true}, function(err3) {
11761176
if (err3) {
11771177
console.log(err3);
11781178
}
@@ -1187,7 +1187,7 @@ const processRequest = (params) => {
11871187
unsetUs["meta_v2." + obj.list[p]] = "";
11881188
}
11891189
//clears out meta data for segments
1190-
common.db.collection("events" + collectionNameWoPrefix).update({$or: my_query}, {$unset: unsetUs}, {multi: true}, function(err4) {
1190+
common.db.collection("events_data").update({"_id": {"$regex": ("^" + params.qstring.app_id + "_" + collectionNameWoPrefix + "_.*")}, $or: my_query}, {$unset: unsetUs}, {multi: true}, function(err4) {
11911191
if (err4) {
11921192
console.log(err4);
11931193
}
@@ -1231,7 +1231,6 @@ const processRequest = (params) => {
12311231
else {
12321232
resolve();
12331233
}
1234-
12351234
});
12361235
}
12371236
else {

api/utils/taskmanager.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1080,4 +1080,4 @@ function getResult(callback, options) {
10801080
}
10811081
};
10821082
}
1083-
module.exports = taskmanager;
1083+
module.exports = taskmanager;

bin/scripts/member-managament/delete_old_members.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Promise.all([pluginManager.dbConnection("countly")]).spread(function(countlyDb)
4444
Url: SERVER_URL + "/i/users/delete",
4545
body: {
4646
api_key: API_KEY,
47-
args: JSON.stringify({user_ids: [(data._id + "")]})
47+
args: {user_ids: [data._id + ""]}
4848
}
4949
}, function(data) {
5050
if (data.err) {
@@ -99,8 +99,7 @@ function sendRequest(params, callback) {
9999
const options = {
100100
uri: url.href,
101101
method: params.requestType,
102-
json: true,
103-
body: body,
102+
json: body,
104103
strictSSL: false
105104
};
106105

frontend/express/public/core/user-management/javascripts/countly.views.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -949,8 +949,20 @@
949949
watch: {
950950
'groups': function() {
951951
if (this.groups.length > 0) {
952-
// Remove global admin role if user is assigned to any group
953-
this.$refs.userDrawer.editedObject.global_admin = false;
952+
// Remove global admin role if the assigned groups does not have global admin access
953+
var groupHasGlobalAdmin = false;
954+
955+
this.groups.forEach(function(grpId) {
956+
var group = groupsModel.data().find(function(grp) {
957+
return grpId === grp._id;
958+
});
959+
960+
if (group && group.global_admin === true) {
961+
groupHasGlobalAdmin = true;
962+
}
963+
});
964+
965+
this.$refs.userDrawer.editedObject.global_admin = groupHasGlobalAdmin;
954966
}
955967

956968
if (this.groups.length === 0) {
@@ -1152,4 +1164,4 @@
11521164
countlyVue.container.registerData("user-management/edit-user-drawer", {
11531165
component: Drawer
11541166
});
1155-
})();
1167+
})();

plugins/crashes/api/api.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,12 @@ plugins.setConfigs("crashes", {
464464
}
465465
updateUser.hadAnyNonfatalCrash = report.ts;
466466
}
467-
let updateData = { $inc: {} };
467+
468+
if ('app_version' in report && typeof report.app_version !== 'string') {
469+
report.app_version += '';
470+
}
471+
let updateData = {$inc: {}};
472+
468473
updateData.$inc["data.crashes"] = 1;
469474
if (Object.keys(updateUser).length) {
470475
updateData.$set = updateUser;

plugins/crashes/tests.js

+29
Original file line numberDiff line numberDiff line change
@@ -3144,6 +3144,35 @@ describe('Testing Crashes', function() {
31443144
});
31453145
});
31463146

3147+
describe('Crash app version', async() => {
3148+
it('should process crash app version as string', async() => {
3149+
const crashData = {
3150+
"_error": "error",
3151+
"_app_version": 123, // app version is number
3152+
"_os": "android",
3153+
};
3154+
3155+
await request.get('/i')
3156+
.query({ app_key: APP_KEY, device_id: DEVICE_ID, crash: JSON.stringify(crashData) })
3157+
.expect(200);
3158+
3159+
const crashGroupQuery = JSON.stringify({
3160+
latest_version: { $in: [`${crashData._app_version}`] },
3161+
});
3162+
let crashGroupResponse = await request
3163+
.get('/o')
3164+
.query({ method: 'crashes', api_key: API_KEY_ADMIN, app_id: APP_ID, query: crashGroupQuery });
3165+
const crashGroup = crashGroupResponse.body.aaData[0];
3166+
crashGroupResponse = await request
3167+
.get(`/o?`)
3168+
.query({ method: 'crashes', api_key: API_KEY_ADMIN, app_id: APP_ID, group: crashGroup._id });
3169+
3170+
const crash = crashGroupResponse.body.data[0];
3171+
3172+
crash.app_version.should.equal(`${crashData._app_version}`);
3173+
});
3174+
});
3175+
31473176
describe('Reset app', function() {
31483177
it('should reset data', function(done) {
31493178
var params = {app_id: APP_ID, period: "reset"};

plugins/logger/frontend/public/javascripts/countly.views.js

+40-3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,44 @@
136136
formatExportFunction: function() {
137137
var tableData = this.logsData;
138138
var table = [];
139+
var sanitizeQueryData = function(data) {
140+
try {
141+
// If data is already a string, parse it first
142+
let queryObject = typeof data === 'string' ? JSON.parse(data) : data;
143+
144+
// Handle nested JSON strings within the object
145+
Object.keys(queryObject).forEach(key => {
146+
if (typeof queryObject[key] === 'string') {
147+
// Try to parse if it looks like JSON
148+
if (queryObject[key].startsWith('{') || queryObject[key].startsWith('[')) {
149+
try {
150+
queryObject[key] = JSON.parse(queryObject[key]);
151+
if (typeof queryObject[key] === 'object' && queryObject[key] !== null) {
152+
queryObject[key] = sanitizeQueryData(queryObject[key]);
153+
}
154+
}
155+
catch (e) {
156+
// If parsing fails, keep decoded string
157+
}
158+
}
159+
queryObject[key] = countlyCommon.unescapeHtml(queryObject[key]);
160+
}
161+
else if (typeof queryObject[key] === 'object' && queryObject[key] !== null) {
162+
// Recursively handle nested objects
163+
Object.keys(queryObject[key]).forEach(nestedKey => {
164+
if (typeof queryObject[key][nestedKey] === 'string') {
165+
queryObject[key][nestedKey] = countlyCommon.unescapeHtml(queryObject[key][nestedKey]);
166+
}
167+
});
168+
}
169+
});
170+
return JSON.stringify(queryObject);
171+
}
172+
catch (err) {
173+
return data; // Return original data if processing fails
174+
}
175+
};
176+
139177
for (var i = 0; i < tableData.length; i++) {
140178
var item = {};
141179
item[CV.i18n('logger.requests').toUpperCase()] = countlyCommon.formatTimeAgoText(tableData[i].reqts).text;
@@ -158,16 +196,15 @@
158196
}
159197
if (tableData[i].q) {
160198
try {
161-
item[CV.i18n('logger.request-query').toUpperCase()] = JSON.stringify(tableData[i].q);
199+
item[CV.i18n('logger.request-query').toUpperCase()] = sanitizeQueryData(tableData[i].q);
162200
}
163201
catch (err) {
164202
item[CV.i18n('logger.request-header').toUpperCase()] = "-";
165203
}
166204
}
167205
if (tableData[i].h) {
168206
try {
169-
var stringifiedHeader = JSON.stringify(tableData[i].h);
170-
item["REQUEST HEADER"] = stringifiedHeader.replace(/&quot;/g, '"');
207+
item["REQUEST HEADER"] = sanitizeQueryData(tableData[i].h);
171208
}
172209
catch (err) {
173210
item["REQUEST HEADER"] = "-";

plugins/views/scripts/omitViewSegments.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function getAppList(options, callback) {
110110
if (app_list && app_list.length > 0) {
111111
var listed = [];
112112
for (var z = 0; z < app_list.length; z++) {
113-
listed.push(options.db.ObjectId(app_list[z]));
113+
listed.push(options.db.ObjectID(app_list[z]));
114114
}
115115
query = {_id: {$in: listed}};
116116
}

test/2.api/15.event.managment.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,10 @@ describe('Testing event settings', function() {
402402

403403
it('checking for segmentation in collections(test3)', function(done) {
404404
var collectionNameWoPrefix = crypto.createHash('sha1').update("test3" + APP_ID).digest('hex');
405-
testUtils.db.collection("events" + collectionNameWoPrefix).find({"s": {$in: ["my_segment"]}}).toArray(function(err, res) {
405+
testUtils.db.collection("events_data").find({"_id": {"$regex": ("^" + APP_ID + "_" + collectionNameWoPrefix + "_.*")}, "s": {$in: ["my_segment"]}}).toArray(function(err, res) {
406+
if (err) {
407+
console.log(err);
408+
}
406409
if (res.length == 0) {
407410
done();
408411
}
@@ -465,7 +468,7 @@ describe('Testing event settings', function() {
465468

466469
it('checking for segmentation in collections(test3)', function(done) {
467470
var collectionNameWoPrefix = crypto.createHash('sha1').update("test3" + APP_ID).digest('hex');
468-
testUtils.db.collection("events" + collectionNameWoPrefix).find({"s": {$in: ["my_segment"]}}).toArray(function(err, res) {
471+
testUtils.db.collection("events_data").find({"_id": {"$regex": ("^" + APP_ID + "_" + collectionNameWoPrefix + "_.*")}, "s": {$in: ["my_segment"]}}).toArray(function(err, res) {
469472
if (res.length == 0) {
470473
done();
471474
}
@@ -516,7 +519,7 @@ describe('Testing event settings', function() {
516519

517520
it('checking for segmentation in collections(t1)', function(done) {
518521
var collectionNameWoPrefix = crypto.createHash('sha1').update("t1" + APP_ID).digest('hex');
519-
testUtils.db.collection("events" + collectionNameWoPrefix).find({"s": {$in: ["s"]}}).toArray(function(err, res) {
522+
testUtils.db.collection("events_data").find({"_id": {"$regex": ("^" + APP_ID + "_" + collectionNameWoPrefix + "_.*")}, "s": {$in: ["s"]}}).toArray(function(err, res) {
520523
if (res.length == 0) {
521524
done();
522525
}
@@ -676,7 +679,7 @@ describe('Testing event settings', function() {
676679

677680
it('checking for segmentation in collections(t5)', function(done) {
678681
var collectionNameWoPrefix = crypto.createHash('sha1').update("t5" + APP_ID).digest('hex');
679-
testUtils.db.collection("events" + collectionNameWoPrefix).find({"s": {$in: ["bad_segment"]}}).toArray(function(err, res) {
682+
testUtils.db.collection("events_data").find({"_id": {"$regex": ("^" + APP_ID + "_" + collectionNameWoPrefix + "_.*")}, "s": {$in: ["bad_segment"]}}).toArray(function(err, res) {
680683
if (res.length == 0) {
681684
done();
682685
}

0 commit comments

Comments
 (0)