Compare commits

...

180 Commits

Author SHA1 Message Date
Martin Edenhofer
fd2ad7a88d Fixed typo. 2017-02-15 14:24:58 +01:00
Martin Edenhofer
6630a3f624 Updated CHANGELOG. 2017-02-15 14:23:05 +01:00
Thorsten Eckel
1004691fea Added the possiblity to inherit attribute filter and overwrite/extend via child methods. 2017-02-15 13:23:28 +01:00
Martin Edenhofer
0f6a3ed2a3 Fixed issue #719 - Ticket without Subject is blocked - can't do anything with it ("Title needed”). 2017-02-13 10:26:36 +01:00
Martin Edenhofer
f78f2bf2a9 Fixed typo in filename of migration. 2017-02-09 13:31:34 +01:00
Martin Edenhofer
64a7267fc6 Fixed issue #650 - Unable to process emails with email addresses longer then 140 signs. 2017-02-09 13:23:52 +01:00
Thorsten Eckel
33c371b837 Added backport to skip concerns in model lookup logic. 2017-02-06 12:13:47 +01:00
Martin Edenhofer
7e6335c3cd Moved to whitelist sanitizer. 2017-02-06 12:06:05 +01:00
Thorsten Eckel
6062f6defc Added HTML Sanitzer concern for multi model usage. 2017-02-06 12:05:53 +01:00
Martin Edenhofer
de04f977a9 Fixed issue #740 - Unable to process emails if "Additional follow-up detection" was checked and unchecked again. 2017-02-06 11:49:15 +01:00
Martin Edenhofer
fc58aa79ac Fixed issue #705 - Time accounting works only with dot. Fixed issue #670 - Auto Focus on "Time Accounting" Popupbox. 2017-02-06 00:16:13 +01:00
Martin Edenhofer
c59cd7b992 Fixed issue #730 - "Close Tab" & "Next in Overview" don't work. 2017-02-05 19:26:45 +01:00
Thorsten Eckel
c9c80d3e72 Fixed issue #617 - Prevent attachment preview in browser attachment download. 2017-02-02 08:22:49 +01:00
Martin Edenhofer
be2d6a45ca Fixed issue #659 - Can't update translations via translation table in admin interface. 2017-02-01 23:24:50 +01:00
Martin Edenhofer
2e9a3a4d04 Fixed issue #685 - Feedback form not working in Safari. 2017-01-30 16:20:06 +01:00
Martin Edenhofer
041b03b4f3 Fixed issue #686 - Follow up detection not working if ticket_hook_position "none" is used. 2017-01-30 16:19:49 +01:00
Martin Edenhofer
8dde0f2832 Fixed issue #636 - Customer get forced to save time for time accounting in customer interface - only allow it for agents. 2017-01-17 15:58:41 +01:00
Martin Edenhofer
b1ef5e2d83 Fixed issue #633 - Unable to activate time accounting in admin interface. 2017-01-17 12:41:38 +01:00
Martin Edenhofer
a01cbd60d6 Updated CHANGELOG. 2017-01-17 09:29:51 +01:00
Martin Edenhofer
17eb0f84ec Merge branch 'develop' 2017-01-17 08:56:18 +01:00
Martin Edenhofer
56d0523c1f Merge branch 'develop' 2017-01-04 15:21:24 +01:00
Martin Edenhofer
9fa18758bc Merge branch 'develop' 2016-12-21 14:12:06 +01:00
Martin Edenhofer
d2f2c55b06 Merge branch 'develop' 2016-12-21 08:50:52 +01:00
Martin Edenhofer
f4a409bd6e Merge branch 'develop' 2016-12-21 00:13:59 +01:00
Martin Edenhofer
971b724d4c Merge branch 'develop' 2016-12-18 12:45:13 +01:00
Martin Edenhofer
3263c6f2ba Merge branch 'develop' 2016-12-16 15:49:33 +01:00
Martin Edenhofer
359b026535 Merge branch 'develop' 2016-12-15 17:16:38 +01:00
Martin Edenhofer
c63518fd3e Merge branch 'develop' 2016-12-14 16:06:09 +01:00
Martin Edenhofer
24ba3cff44 Merge branch 'develop' 2016-12-06 13:29:23 +01:00
Martin Edenhofer
f7066e1995 Merge branch 'develop' 2016-12-06 12:23:13 +01:00
Martin Edenhofer
6844499309 Merge branch 'develop' 2016-11-25 16:59:16 +01:00
Martin Edenhofer
afa3e47eab Merge branch 'develop' 2016-11-25 14:22:00 +01:00
Martin Edenhofer
5f3e9298f1 Merge branch 'develop' 2016-11-24 16:03:45 +01:00
Martin Edenhofer
e4dd303357 Merge branch 'develop' 2016-11-21 17:07:30 +01:00
Martin Edenhofer
2fa06eee3d Merge branch 'develop' 2016-11-21 08:58:54 +01:00
Martin Edenhofer
dc0ac03334 Merge branch 'develop' 2016-11-19 16:42:10 +01:00
Martin Edenhofer
c5a1d69ec5 Merge branch 'develop' 2016-11-19 16:35:07 +01:00
Martin Edenhofer
9891981f92 Merge branch 'develop' 2016-11-17 15:12:22 +01:00
Martin Edenhofer
2c721f71e7 Merge branch 'develop' 2016-11-17 10:03:34 +01:00
Martin Edenhofer
0b4a4be0b7 Merge branch 'develop' 2016-11-16 23:21:05 +01:00
Martin Edenhofer
b13bb2fcaf Added change log. 2016-11-14 08:37:51 +01:00
Martin Edenhofer
d9ba369fcf Merge branch 'develop' 2016-11-14 08:36:58 +01:00
Martin Edenhofer
3adece0db5 Merge branch 'develop' 2016-11-14 00:03:57 +01:00
Martin Edenhofer
7101ffc277 Merge branch 'develop' 2016-11-12 16:44:43 +01:00
Martin Edenhofer
19a7a439b8 Merge branch 'develop' 2016-11-11 19:29:56 +01:00
Martin Edenhofer
e728320241 Merge branch 'develop' 2016-10-30 07:08:08 +01:00
Martin Edenhofer
1bc17afc53 Merge branch 'master' of git.znuny.com:zammad/zammad 2016-10-30 07:07:39 +01:00
Thorsten Eckel
ee5c58f53a Fixed bug: Zip archive doesn't contain sub folder content. 2016-10-19 08:25:51 +02:00
Martin Edenhofer
9bdf6d7b81 Merge branch 'develop' 2016-10-19 07:50:08 +02:00
Thorsten Eckel
5754fdccdd Working on CI tag/branch pushing, syncing, deployment issue. 2016-10-19 07:32:05 +02:00
Thorsten Eckel
4901050d57 Working on issue not syncing to github correctly. 2016-10-19 07:25:18 +02:00
Martin Edenhofer
7980c8cb93 Merge branch 'develop' 2016-10-19 07:21:10 +02:00
Martin Edenhofer
7e7088388d Merge branch 'develop' 2016-10-17 16:43:54 +02:00
Martin Edenhofer
2f6e52d63d Merge branch 'develop' 2016-09-28 16:08:28 +02:00
Martin Edenhofer
bde8babb43 Merge branch 'develop' 2016-09-28 08:53:02 +02:00
Martin Edenhofer
d6d68fb363 Merge branch 'develop' 2016-09-19 07:47:06 +02:00
Martin Edenhofer
814ffed0f0 Merge branch 'develop' 2016-09-15 22:11:57 +02:00
Thorsten Eckel
b013fb8cbc Merge branch 'private-otrs_import_df' 2016-09-13 12:06:56 +02:00
Martin Edenhofer
fbd8e63592 Merge branch 'develop' 2016-09-13 08:55:38 +02:00
Martin Edenhofer
e8ad3965d7 Removed build badged. 2016-09-10 16:47:33 +02:00
Martin Edenhofer
e00981ee23 Merge branch 'master' of git.znuny.com:zammad/zammad 2016-09-10 00:05:18 +02:00
Martin Edenhofer
4e74c6beda Merge branch 'develop' 2016-09-10 00:04:28 +02:00
Thorsten Eckel
2d09e556d7 Added deployment of archives. 2016-09-07 15:31:28 +02:00
Martin Edenhofer
8cd39398d6 Merge branch 'develop' 2016-09-06 16:07:33 +02:00
Martin Edenhofer
94c616ffe3 Merge branch 'develop' 2016-08-25 10:00:36 +02:00
Martin Edenhofer
57649bb888 Merge branch 'develop' 2016-08-23 09:49:40 +02:00
Martin Edenhofer
7e9442ec4a Merge branch 'develop' 2016-08-21 21:38:00 +02:00
Martin Edenhofer
521e6265fd Merge branch 'develop' 2016-08-19 13:58:26 +02:00
Martin Edenhofer
a42a9689fc Merge branch 'develop' 2016-08-19 13:48:46 +02:00
Martin Edenhofer
d9ffb8001d Merge branch 'develop' 2016-07-28 13:39:08 +02:00
Martin Edenhofer
ec86d26ae6 Merge branch 'develop' 2016-07-24 23:52:04 +02:00
Martin Edenhofer
e00efc821a Merge branch 'develop' 2016-07-18 19:35:51 +02:00
Martin Edenhofer
f04be8ca93 Merge branch 'develop' 2016-07-12 20:50:32 +02:00
Martin Edenhofer
c7ff6a323a Merge branch 'develop' 2016-07-08 11:28:43 +02:00
Martin Edenhofer
34833416bc Merge branch 'develop' 2016-06-30 09:50:09 +02:00
Martin Edenhofer
d7a6e28d81 Merge branch 'develop' 2016-06-29 00:18:44 +02:00
Martin Edenhofer
c13531d720 Merge branch 'develop' 2016-06-27 11:28:22 +02:00
Martin Edenhofer
4a40cced7d Merge branch 'develop' 2016-06-26 08:07:04 +02:00
Martin Edenhofer
450de10cc7 Merge branch 'develop' 2016-06-23 08:24:56 +02:00
Martin Edenhofer
5e587a455c Merge branch 'develop' 2016-06-22 08:54:24 +02:00
Martin Edenhofer
04aef9b258 Merge branch 'develop' 2016-06-20 15:52:43 +02:00
Martin Edenhofer
9b72557d13 Merge branch 'develop' 2016-06-15 06:58:36 +02:00
Martin Edenhofer
a5e0bc30c1 Merge branch 'develop' 2016-06-09 13:40:27 +02:00
Martin Edenhofer
458c166e9f Merge branch 'develop' 2016-06-08 16:43:29 +02:00
Martin Edenhofer
3373e5d141 Merge branch 'develop' 2016-06-08 10:48:12 +02:00
Martin Edenhofer
8f6b8de617 Merge branch 'develop' 2016-06-08 07:42:19 +02:00
Martin Edenhofer
22b21a82e7 Merge branch 'develop' 2016-06-07 07:02:25 +02:00
Martin Edenhofer
b4151b299f Merge branch 'develop' 2016-06-04 02:23:14 +02:00
Martin Edenhofer
e969fca8c4 Merge branch 'develop' 2016-06-03 07:08:59 +02:00
Martin Edenhofer
e59993a5d6 Merge branch 'develop' 2016-05-31 08:31:56 +02:00
Martin Edenhofer
3adc9d4cf1 Merge branch 'develop' 2016-05-30 13:21:46 +02:00
Martin Edenhofer
c1153250cb Merge branch 'develop' 2016-05-30 00:00:52 +02:00
Martin Edenhofer
7718dc28d3 Merge branch 'develop'
# Conflicts:
#	README.md
2016-05-27 17:30:22 +02:00
Martin Edenhofer
1894580ae6 Updated badge url. 2016-05-27 17:28:26 +02:00
Martin Edenhofer
11df714bbf Merge branch 'develop' 2016-05-20 15:18:36 +02:00
Martin Edenhofer
72adee37a8 Merge branch 'develop' 2016-05-20 06:10:19 +02:00
Martin Edenhofer
0e8f460d73 Merge branch 'develop' 2016-05-06 16:25:09 +02:00
Martin Edenhofer
4e62d5d0d2 Merge branch 'develop' 2016-05-06 09:40:16 +02:00
Martin Edenhofer
004e5ba457 Merge branch 'develop' 2016-05-04 12:13:34 +02:00
Martin Edenhofer
f1a8a8da6f Merge branch 'develop' 2016-05-03 16:01:03 +02:00
Martin Edenhofer
bfb44d1189 Merge branch 'develop' 2016-05-01 14:18:26 +02:00
Martin Edenhofer
d64afd8d97 Merge branch 'develop' 2016-04-14 17:47:03 +02:00
Martin Edenhofer
97cfdb9c4c Merge branch 'develop' 2016-04-12 08:25:06 +02:00
Martin Edenhofer
bb336f63f1 Merge branch 'develop' 2016-03-25 18:23:12 +01:00
Martin Edenhofer
80da08f447 Merge branch 'develop' 2016-03-23 09:31:02 +01:00
Martin Edenhofer
a6db2b7c90 Merge branch 'develop' 2016-03-17 17:45:04 +01:00
Martin Edenhofer
e1c3f83324 Merge branch 'develop' 2016-03-17 10:41:20 +01:00
Martin Edenhofer
8545a44ad1 Merge branch 'develop' 2016-03-16 11:17:22 +01:00
Martin Edenhofer
cfbe7510a2 Merge branch 'develop' 2016-03-16 09:10:05 +01:00
Martin Edenhofer
1479c32edb Merge branch 'develop' 2016-03-15 00:34:36 +01:00
Martin Edenhofer
a9d428bf5a Merge branch 'develop' 2016-03-11 10:38:15 +01:00
Martin Edenhofer
3a62eb11da Merge branch 'develop' 2016-03-04 07:07:55 +01:00
Martin Edenhofer
2df5f81029 Merge branch 'develop' 2016-03-03 03:42:27 +01:00
Martin Edenhofer
b88c23bc92 Merge branch 'develop' 2016-02-28 00:16:49 +01:00
Martin Edenhofer
4144fbaf5b Merge branch 'develop' 2016-02-25 23:46:10 +01:00
Martin Edenhofer
86ddc93f52 Merge branch 'develop' 2016-02-23 14:29:01 +01:00
Martin Edenhofer
8a1b583535 Merge branch 'develop' 2016-02-23 01:38:39 +01:00
Martin Edenhofer
7623628b86 Merge branch 'develop' 2016-02-12 08:53:57 +01:00
Martin Edenhofer
f753bc203f Merge branch 'develop' 2016-02-11 13:25:39 +01:00
Martin Edenhofer
ff9ba10705 Merge branch 'develop' 2016-02-11 02:11:06 +01:00
Martin Edenhofer
fdc25bdc26 Merge branch 'develop' 2016-02-09 01:36:03 +01:00
Martin Edenhofer
d154a587ad Merge branch 'develop' 2016-02-04 12:55:30 +01:00
Martin Edenhofer
a9cc4f037b Merge branch 'develop' 2016-02-04 10:41:41 +01:00
Martin Edenhofer
50d6679189 Merge branch 'develop' 2016-02-03 13:19:51 +01:00
Martin Edenhofer
b82cf4e1d6 Merge branch 'develop' 2016-01-29 15:19:23 +01:00
Martin Edenhofer
fa6eaa6710 Merge branch 'develop'
# Conflicts:
#	README.md
2016-01-26 19:59:34 +01:00
Martin Edenhofer
2ca17ac1bd Updated build status url. 2016-01-26 13:57:24 +01:00
Martin Edenhofer
862ecd7d36 Merge branch 'develop' 2016-01-22 14:47:53 +01:00
Martin Edenhofer
e38b72593b Merge branch 'develop' 2016-01-12 11:14:24 +01:00
Martin Edenhofer
81883cae8f Revert "update terminal start script"
This reverts commit 63770c9cbf.
2016-01-12 11:13:40 +01:00
Felix Niklas
63770c9cbf update terminal start script 2016-01-12 10:58:46 +01:00
Martin Edenhofer
aaf5da55a0 Merge branch 'develop' 2015-12-28 11:33:58 +01:00
Martin Edenhofer
8455d9ae17 Merge branch 'develop' 2015-12-21 16:03:31 +01:00
Martin Edenhofer
839a8e8eb4 Merge branch 'develop' 2015-12-13 15:26:35 +01:00
Martin Edenhofer
7aa3f6eaee Updated build url. 2015-12-10 09:44:16 +01:00
Martin Edenhofer
219abc69a3 Merge branch 'develop' 2015-12-10 09:21:59 +01:00
Martin Edenhofer
595822c928 Merge branch 'develop' 2015-12-02 02:27:40 +01:00
Martin Edenhofer
32112937b1 Merge branch 'develop' 2015-12-02 01:41:10 +01:00
Martin Edenhofer
a0889c5b99 Merge branch 'develop' 2015-12-01 22:03:52 +01:00
Martin Edenhofer
4bc281bfb0 Merge branch 'develop' 2015-12-01 20:04:51 +01:00
Martin Edenhofer
e12ec607d7 Merge branch 'develop' 2015-11-30 15:11:25 +01:00
Martin Edenhofer
545f393293 Merge branch 'develop' 2015-11-30 01:24:11 +01:00
Martin Edenhofer
4efa152e93 Merge branch 'master' of github.com:martini/zammad 2015-11-24 03:39:17 +01:00
Martin Edenhofer
0ebda4f32a Merge branch 'develop'
Conflicts:
	README.md
	script/websocket-server.rb
2015-11-24 03:38:55 +01:00
Martin Edenhofer
b94f54799f Merge branch 'develop' of http://ci.lab.zammad.com/core/zammad 2015-11-24 03:07:38 +01:00
Martin Edenhofer
99f0b4e2e2 Merge branch 'develop' 2015-11-19 14:27:39 +01:00
Martin Edenhofer
53cb906733 Merge branch 'develop' 2015-11-01 01:49:02 +01:00
Martin Edenhofer
2c31fe8a84 Merge branch 'develop' 2015-10-31 23:40:10 +01:00
Martin Edenhofer
6c5e673a2c Merge branch 'develop' 2015-10-02 15:31:54 +02:00
Martin Edenhofer
574b891a33 Merge branch 'develop' 2015-09-28 02:12:31 +02:00
Martin Edenhofer
9ef1d3f58b Merge branch 'develop'
Conflicts:
	extras/zammad.spec
2015-09-28 01:24:03 +02:00
Martin Edenhofer
8b3f432746 Merge branch 'develop' 2015-09-01 03:15:03 +02:00
Martin Edenhofer
14a9df261e Merge branch 'develop' 2015-08-31 15:11:27 +02:00
Martin Edenhofer
5620a2847b Merge branch 'develop' 2015-08-31 12:42:04 +02:00
Martin Edenhofer
13aaae3835 Merge branch 'develop' 2015-08-30 09:19:02 +02:00
Martin Edenhofer
2d4fcd3a4f Merge branch 'develop' 2015-08-30 08:22:27 +02:00
Martin Edenhofer
f4f70b0bc2 Merge branch 'develop' 2015-08-30 01:36:18 +02:00
Martin Edenhofer
1d279bb88f Merge branch 'develop' 2015-08-30 01:32:10 +02:00
Martin Edenhofer
fe4757cbf9 Merge branch 'develop' 2015-08-21 17:03:28 +02:00
me
1d476f163e Use also mysql on test. 2015-08-21 16:53:38 +02:00
Martin Edenhofer
513701b6d2 Merge branch 'develop'
Conflicts:
	README.md
	script/websocket-server.rb
2015-08-11 15:17:14 +02:00
Thorsten Eckel
1a1b661714 Fixed typo. 2015-07-16 09:50:18 +02:00
Roy Kaldung
6081a98279 Update README.md 2015-01-12 12:39:26 +01:00
Roy Kaldung
157991c33d Update README.md 2015-01-12 12:38:11 +01:00
Roy Kaldung
ef67be9170 We like to know if it's starting or stopping 2014-11-06 17:24:45 +01:00
Martin Edenhofer
39f3819303 Merge branch 'develop' 2014-11-06 16:13:39 +01:00
Martin Edenhofer
7a676bd563 Merge branch 'develop' into 'master', goto next level.
Conflicts:
	Gemfile
	README.md
	extras/zammad.spec
2014-10-05 20:42:39 +02:00
Roy Kaldung
80c54daa03 Delete httpd.spec 2014-05-02 12:47:51 +02:00
Roy Kaldung
99c88cde62 Delete apr.spec 2014-05-02 12:47:41 +02:00
Roy Kaldung
17d113ca20 removed old file 2014-05-02 12:47:18 +02:00
Martin Edenhofer
2fbf4324a7 Update Gemfile 2014-02-22 14:46:25 +01:00
Roy Kaldung
b46c390d30 minor change to test GitHub hook (automatic deploy) 2013-12-04 18:22:21 +01:00
Martin Edenhofer
3c3651bc52 Merge branch 'develop'
Conflicts:
	app/controllers/tickets_controller.rb
2013-09-20 13:52:53 +02:00
Roy Kaldung
abeeb5e66d Updated Build Status image URL 2013-08-23 21:00:55 +02:00
rkaldung
09d61ce4c8 added dependency 2013-08-14 20:38:09 +02:00
rkaldung
cc1c4495b2 Merge branch 'master' of github.com:martini/zammad 2013-08-14 20:27:15 +02:00
rkaldung
7b1ec4d17f modified install section 2013-08-14 20:26:25 +02:00
rkaldung
99b3201ccf added initial spec for rpm 2013-08-14 20:20:55 +02:00
Roy Kaldung
d4205d9ba6 removed Windows ;-) 2013-08-14 20:08:23 +02:00
Roy Kaldung
8d57633390 minor changes on comments 2013-08-05 19:18:52 +02:00
30 changed files with 1099 additions and 50 deletions

96
CHANGELOG.md Normal file
View File

@@ -0,0 +1,96 @@
# Change Log
## [1.2.1](https://github.com/zammad/zammad/tree/1.2.1) (2017-02-15)
[Full Changelog](https://github.com/zammad/zammad/compare/1.2.0...1.2.1)
**Fixed bugs:**
- Ticket without Subject is blocked - can't do anything with it \("Title needed"\) [\#719](https://github.com/zammad/zammad/issues/719) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Unable to process emails with email addresses longer then 140 signs. [\#650](https://github.com/zammad/zammad/issues/650) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Unable to process emails if "Additional follow-up detection" was checked and unchecked again [\#740](https://github.com/zammad/zammad/issues/740) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Time accounting works only with dot [\#705](https://github.com/zammad/zammad/issues/705) [[bug](https://github.com/zammad/zammad/labels/bug)]
- "Close Tab" & "Next in Overview" don't work [\#730](https://github.com/zammad/zammad/issues/730) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Prevent attachment preview in browser attachment download [\#617](https://github.com/zammad/zammad/issues/617) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Feedback form not working in Safari [\#685](https://github.com/zammad/zammad/issues/685) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Follow up detection not working if ticket\_hook\_position "none" is used. [\#686](https://github.com/zammad/zammad/issues/686) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Customer get forced to save time for time accounting in customer interface - only allow it for agents. [\#636](https://github.com/zammad/zammad/issues/636) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Unable to activate time accounting in admin interface. [\#633](https://github.com/zammad/zammad/issues/633) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Prevent attachment preview in browser attachment download [\#617](https://github.com/zammad/zammad/issues/617) [[bug](https://github.com/zammad/zammad/labels/bug)]
## [1.2.0](https://github.com/zammad/zammad/tree/1.2.0) (2017-01-16)
[Full Changelog](https://github.com/zammad/zammad/compare/1.1.0...1.2.0)
**Implemented enhancements:**
- Feature "Time recording" [\#373](https://github.com/zammad/zammad/issues/373) [[feature](https://github.com/zammad/zammad/labels/feature)]
- Proxy support [\#439](https://github.com/zammad/zammad/issues/439) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Improved call log workflow [\#517](https://github.com/zammad/zammad/pull/517) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] ([TeaMoe](https://github.com/TeaMoe))
- Create ticket with customer [\#514](https://github.com/zammad/zammad/pull/514) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)] ([TeaMoe](https://github.com/TeaMoe))
- Versions françaises [\#578](https://github.com/zammad/zammad/pull/578) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)] [[translation](https://github.com/zammad/zammad/labels/translation)] ([GeraldElbaze-Medias-Cite](https://github.com/GeraldElbaze-Medias-Cite))
- French for Chat widget [\#580](https://github.com/zammad/zammad/issues/580) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Show other active agents in ticket detail view \(collision prevention\) [\#352](https://github.com/zammad/zammad/issues/352) [[feature](https://github.com/zammad/zammad/labels/feature)] [[UX/UI](https://github.com/zammad/zammad/labels/UX/UI)]
**Fixed bugs:**
- OTRS import - long content type shows warning and gets cut [\#582](https://github.com/zammad/zammad/issues/582) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Setup asks for FQDN, but doesn't write to DB [\#462](https://github.com/zammad/zammad/issues/462) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Group restrictions not respected in slack notification [\#587](https://github.com/zammad/zammad/issues/587) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Keyboard shortcuts dialog breaks url bar [\#530](https://github.com/zammad/zammad/issues/530) [[bug](https://github.com/zammad/zammad/labels/bug)]
- OTRS import - unsupported DynamicField type fields cause import to abort [\#565](https://github.com/zammad/zammad/issues/565) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Gitlab Auth not working [\#596](https://github.com/zammad/zammad/issues/596) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Admin user login via Oauth [\#602](https://github.com/zammad/zammad/issues/602) [[bug](https://github.com/zammad/zammad/labels/bug)]
- If "new ticket created" popover is shown you can't click search box [\#597](https://github.com/zammad/zammad/issues/597) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- New ticket attributes are not shown for customer [\#576](https://github.com/zammad/zammad/issues/576) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Imap sort errors with gmail in production.log [\#568](https://github.com/zammad/zammad/issues/568) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Agent - Group assignment shows "\<br\>" [\#566](https://github.com/zammad/zammad/issues/566) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Renaming standard roles breaks ticket views [\#499](https://github.com/zammad/zammad/issues/499) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Overview is not show it organization is used as row and first ticket has no organization \(app is not responding anymore\) [\#554](https://github.com/zammad/zammad/issues/554) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Add organization to overview in manage users \(admin interface\) [\#552](https://github.com/zammad/zammad/issues/552) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- OTRS import: Empty created\_at of customer user record brakes import. [\#548](https://github.com/zammad/zammad/issues/548) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Layout broken if outbound channel has a error message [\#547](https://github.com/zammad/zammad/issues/547) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Do not show arrow down on ajax auto completion fields [\#546](https://github.com/zammad/zammad/issues/546) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Error message at creation of new overview with not existing specific customer [\#545](https://github.com/zammad/zammad/issues/545) [[bug](https://github.com/zammad/zammad/labels/bug)]
## [1.1.0](https://github.com/zammad/zammad/tree/1.1.0) (2016-11-14)
[Full Changelog](https://github.com/zammad/zammad/compare/1.0.1...1.1.0)
**Implemented enhancements:**
- Missing translation/edit abilities \(Notification mails, contact form\) [\#388](https://github.com/zammad/zammad/issues/388) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Automatically join users to organizations based on the email address [\#356](https://github.com/zammad/zammad/issues/356) [[feature](https://github.com/zammad/zammad/labels/feature)]
- Create emails via fetchmail / procmail [\#326](https://github.com/zammad/zammad/issues/326) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Display origin url from tweet/facebook post [\#317](https://github.com/zammad/zammad/issues/317) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
- Vagrant version [\#253](https://github.com/zammad/zammad/issues/253) [[feature](https://github.com/zammad/zammad/labels/feature)]
- Page posts from facebook and tweets from twitter as tickets [\#251](https://github.com/zammad/zammad/issues/251) [[enhancement](https://github.com/zammad/zammad/labels/enhancement)]
**Fixed bugs:**
- Some windows in the config area too wide in firefox [\#392](https://github.com/zammad/zammad/issues/392) [[bug](https://github.com/zammad/zammad/labels/bug)]
- \[Critical\] Short display of "Setup a new system" on public site while restarting Zammad! [\#389](https://github.com/zammad/zammad/issues/389) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Mailresponse to customer not working at all after editing a "Trigger" event text [\#385](https://github.com/zammad/zammad/issues/385) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Click on "Reports" leads to Error 500 [\#384](https://github.com/zammad/zammad/issues/384) [[bug](https://github.com/zammad/zammad/labels/bug)]
- TicketSelector in Overview or SLA not working with filter for ticket customer or owner [\#383](https://github.com/zammad/zammad/issues/383) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Changelog generator breaks packager.io builds [\#379](https://github.com/zammad/zammad/issues/379) [[bug](https://github.com/zammad/zammad/labels/bug)]
- script/scheduler.rb crashes after recreation of Zammad database [\#374](https://github.com/zammad/zammad/issues/374) [[bug](https://github.com/zammad/zammad/labels/bug)]
- ticket search cache issue [\#349](https://github.com/zammad/zammad/issues/349) [[bug](https://github.com/zammad/zammad/labels/bug)]
- IMAP mail fetching stops because of broken spam email \(invalid Content-Transfer-Encoding header\) [\#348](https://github.com/zammad/zammad/issues/348) [[bug](https://github.com/zammad/zammad/labels/bug)]
- IMAP mail fetching stops working on spam mail and invalid email format [\#345](https://github.com/zammad/zammad/issues/345) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Unable to create tickets [\#323](https://github.com/zammad/zammad/issues/323) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Admin -\> Channels -\> Email -\> Filters not shown / no name shown [\#313](https://github.com/zammad/zammad/issues/313) [[bug](https://github.com/zammad/zammad/labels/bug)]
- In object manager, blank line in object deleted all values for selections [\#312](https://github.com/zammad/zammad/issues/312) [[bug](https://github.com/zammad/zammad/labels/bug)]
- triggers do not work? [\#306](https://github.com/zammad/zammad/issues/306) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Answers from google mail shows complete quote [\#286](https://github.com/zammad/zammad/issues/286) [[minor bug](https://github.com/zammad/zammad/labels/minor%20bug)]
- Avatar in Chat not showing - wrong url [\#275](https://github.com/zammad/zammad/issues/275) [[bug](https://github.com/zammad/zammad/labels/bug)]
- chat not working / http 404 [\#274](https://github.com/zammad/zammad/issues/274) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Zammad Docker container for rpi/ARM [\#271](https://github.com/zammad/zammad/issues/271) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Zendesk: unknown article type fails import. [\#270](https://github.com/zammad/zammad/issues/270) [[bug](https://github.com/zammad/zammad/labels/bug)]
- No space at comment on ticket bulk action [\#268](https://github.com/zammad/zammad/issues/268) [[bug](https://github.com/zammad/zammad/labels/bug)]
- After adding a new attribute - zammad won't start [\#248](https://github.com/zammad/zammad/issues/248) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Images/Avatars blank [\#217](https://github.com/zammad/zammad/issues/217) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Docker/Ubuntu Not starting postgresql-9.6 service [\#213](https://github.com/zammad/zammad/issues/213) [[bug](https://github.com/zammad/zammad/labels/bug)]
- Can´t choose Organization for Customer [\#211](https://github.com/zammad/zammad/issues/211) [[bug](https://github.com/zammad/zammad/labels/bug)]
- CentOS 7 HTTPS Error 404 - Not Found [\#210](https://github.com/zammad/zammad/issues/210) [[bug](https://github.com/zammad/zammad/labels/bug)]
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@@ -678,6 +678,7 @@ class App.ControllerModal extends App.Controller
container: null
buttonClass: 'btn--success'
centerButtons: []
leftButtons: []
buttonClose: true
buttonCancel: false
buttonCancelClass: 'btn--text btn--subtle'
@@ -729,6 +730,7 @@ class App.ControllerModal extends App.Controller
buttonSubmit: @buttonSubmit
buttonClass: @buttonClass
centerButtons: @centerButtons
leftButtons: @leftButtons
)
modal.find('.modal-body').html content
if !@initRenderingDone

View File

@@ -1096,6 +1096,11 @@ class BulkForm extends App.Controller
return
ticket.load(ticket_update)
# if title is empty - ticket can't processed, set ?
if _.isEmpty(ticket.title)
ticket.title = '-'
ticket.save(
done: (r) =>
@bulk_count_index++

View File

@@ -660,8 +660,6 @@ class App.TicketZoom extends App.Controller
@formEnable(e)
return
taskAction = @$('.js-secondaryActionButtonLabel').data('type')
ticketParams = @formParam(@$('.edit'))
# validate ticket
@@ -705,11 +703,9 @@ class App.TicketZoom extends App.Controller
if !ticket['owner_id']
ticket['owner_id'] = 1
# check if title exists
if !ticket['title']
alert( App.i18n.translateContent('Title needed') )
@formEnable(e)
return
# if title is empty - ticket can't processed, set ?
if _.isEmpty(ticket.title)
ticket.title = '-'
# stop autosave
@autosaveStop()
@@ -763,6 +759,10 @@ class App.TicketZoom extends App.Controller
return
# time tracking
if @permissionCheck('ticket.customer')
@submitPost(e, ticket)
return
new App.TicketZoomTimeAccounting(
container: @el.closest('.content')
ticket: ticket
@@ -776,6 +776,8 @@ class App.TicketZoom extends App.Controller
submitPost: (e, ticket) =>
taskAction = @$('.js-secondaryActionButtonLabel').data('type')
# submit changes
@ajax(
id: "ticket_update_#{ticket.id}"

View File

@@ -1,20 +1,47 @@
class App.TicketZoomTimeAccounting extends App.ControllerModal
buttonClose: true
buttonCancel: 'skip'
buttonCancel: false
buttonSubmit: 'Account Time'
buttonClass: 'btn--success'
leftButtons: [{
className: 'btn--text btn--subtle js-skip',
text: 'skip'
}]
head: 'Time Accounting'
small: true
events:
'submit form': 'submit'
'click .js-submit:not(.is-disabled)': 'submit'
'click .js-cancel': 'cancel'
'click .js-close': 'cancel'
'click .js-skip': 'skip'
content: ->
App.view('ticket_zoom/time_accounting')()
onShown: =>
@$('[name="time_unit"]').focus()
skip: (e) =>
return if !@submitCallback
@submitCallback({})
@close(e)
onCancel: =>
if @cancelCallback
@cancelCallback()
return if !@cancelCallback
@cancelCallback()
onSubmit: =>
@$('[name="time_unit"]').removeClass('has-error')
params = @formParams()
if params.time_unit
params.time_unit = params.time_unit.replace(',', '.')
if isNaN(parseFloat(params.time_unit)) || params.time_unit.match(/[A-z]|\s/)
@$('[name="time_unit"]').addClass('has-error')
return
@close()
if @submitCallback
params = @formParams()
@submitCallback(params)

View File

@@ -282,9 +282,12 @@ class _i18nSingleton extends Spine.Module
notTranslated
removeNotTranslated: (locale, key) =>
return if !@_notTranslated[locale]
delete @_notTranslated[locale][key]
setNotTranslated: (locale, key) =>
if !@_notTranslated[locale]
@_notTranslated[locale] = {}
@_notTranslated[locale][key] = true
date: (time, offset) =>

View File

@@ -15,8 +15,14 @@
<div class="modal-footer">
<% if @buttonCancel: %>
<div class="modal-leftFooter">
<a class="btn <%= @buttonCancelClass %> js-cancel align-left" href="#/"><%- @T(@buttonCancel) %></a>
<a class="btn <%= @buttonCancelClass %> js-cancel align-left" href="#"><%- @T(@buttonCancel) %></a>
</div>
<% else if @leftButtons: %>
<% for button in @leftButtons: %>
<div class="modal-leftFooter">
<div class="btn <%= button.className %> align-left" href="#"><%- @T(button.text) %></div>
</div>
<% end %>
<% end %>
<% for button in @centerButtons: %>
<div class="modal-centerFooter">

View File

@@ -51,7 +51,7 @@
<div class="attachments-title"><%- @article.attachments.length %> <%- @T('Attached Files') %></div>
<% for attachment in @article.attachments: %>
<div class="attachment">
<a class="attachment-name u-highlight" href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= @article.ticket_id %>/<%= @article.id %>/<%= attachment.id %>" target="_blank" data-type="attachment"><%= attachment.filename %></a>
<a class="attachment-name u-highlight" href="<%= App.Config.get('api_path') %>/ticket_attachment/<%= @article.ticket_id %>/<%= @article.id %>/<%= attachment.id %>?disposition=attachment" target="_blank" data-type="attachment"><%= attachment.filename %></a>
<div class="attachment-size"><%- @humanFileSize(attachment.size) %></div>
</div>
<% end %>

View File

@@ -239,11 +239,14 @@ class TicketArticlesController < ApplicationController
# find file
file = Store.find(params[:id])
disposition = sanitized_disposition
send_data(
file.content,
filename: file.filename,
type: file.preferences['Content-Type'] || file.preferences['Mime-Type'],
disposition: 'inline'
disposition: disposition
)
end
@@ -267,4 +270,12 @@ class TicketArticlesController < ApplicationController
)
end
private
def sanitized_disposition
disposition = params.fetch(:disposition, 'inline')
valid_disposition = %w(inline attachment)
return disposition if valid_disposition.include?(disposition)
raise Exceptions::NotAuthorized, "Invalid disposition #{disposition} requested. Only #{valid_disposition.join(', ')} are valid."
end
end

View File

@@ -212,6 +212,9 @@ returns
next if !respond_to?(real_ids)
attributes[real_ids] = send(real_ids)
}
filter_attributes(attributes)
Cache.write(key, attributes)
attributes
end
@@ -271,12 +274,16 @@ returns
attributes[destination] = user.login
}
filter_attributes(attributes)
attributes
end
def filter_attributes(attributes)
# remove forbitten attributes
%w(password token tokens token_ids).each { |item|
attributes.delete(item)
}
attributes
end
=begin

View File

@@ -79,14 +79,14 @@ class Channel::EmailParser
data[field.name.to_s.downcase.to_sym] = Encode.conv('utf8', field.to_s)
# if we need to access the lines by objects later again
data[ "raw-#{field.name.downcase}".to_sym ] = field
data["raw-#{field.name.downcase}".to_sym] = field
}
# get sender
from = nil
['from', 'reply-to', 'return-path'].each { |item|
next if !mail[ item.to_sym ]
from = mail[ item.to_sym ].value
next if !mail[item.to_sym]
from = mail[item.to_sym].value
break if from
}
@@ -376,7 +376,7 @@ returns
do not raise an exception - e. g. if used by scheduler
parser = Channel::EmailParser.new
ticket, article, user = parser.process(channel, email_raw_string, fakse)
ticket, article, user = parser.process(channel, email_raw_string, false)
returns
@@ -430,7 +430,7 @@ returns
}
# check ignore header
if mail[ 'x-zammad-ignore'.to_sym ] == 'true' || mail[ 'x-zammad-ignore'.to_sym ] == true
if mail['x-zammad-ignore'.to_sym] == 'true' || mail['x-zammad-ignore'.to_sym] == true
Rails.logger.info "ignored email with msgid '#{mail[:message_id]}' from '#{mail[:from]}' because of x-zammad-ignore header"
return true
end
@@ -446,7 +446,7 @@ returns
Transaction.execute(interface_handle: "#{original_interface_handle}.postmaster") do
# get sender user
session_user_id = mail[ 'x-zammad-session-user-id'.to_sym ]
session_user_id = mail['x-zammad-session-user-id'.to_sym]
if !session_user_id
raise 'No x-zammad-session-user-id, no sender set!'
end
@@ -459,11 +459,11 @@ returns
UserInfo.current_user_id = session_user.id
# get ticket# based on email headers
if mail[ 'x-zammad-ticket-id'.to_sym ]
ticket = Ticket.find_by(id: mail[ 'x-zammad-ticket-id'.to_sym ])
if mail['x-zammad-ticket-id'.to_sym]
ticket = Ticket.find_by(id: mail['x-zammad-ticket-id'.to_sym])
end
if mail[ 'x-zammad-ticket-number'.to_sym ]
ticket = Ticket.find_by(number: mail[ 'x-zammad-ticket-number'.to_sym ])
if mail['x-zammad-ticket-number'.to_sym]
ticket = Ticket.find_by(number: mail['x-zammad-ticket-number'.to_sym])
end
# set ticket state to open if not new
@@ -482,8 +482,8 @@ returns
end
# set ticket to open again
if !mail[ 'x-zammad-ticket-followup-state'.to_sym ]
if state_type.name != 'new' && !mail[ 'x-zammad-out-of-office'.to_sym ]
if !mail['x-zammad-ticket-followup-state'.to_sym] && !mail['x-zammad-ticket-followup-state_id'.to_sym]
if state_type.name != 'new' && !mail['x-zammad-out-of-office'.to_sym]
ticket.state = Ticket::State.find_by(name: 'open')
ticket.save!
end
@@ -626,25 +626,25 @@ returns
# only set value on _id if value/reference lookup exists
if mail[ header.to_sym ]
Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}"
Rails.logger.info "header #{header} found #{mail[header.to_sym]}"
item_object.class.reflect_on_all_associations.map { |assoc|
next if assoc.name.to_s != key_short
Rails.logger.info "ASSOC found #{assoc.class_name} lookup #{mail[ header.to_sym ]}"
Rails.logger.info "ASSOC found #{assoc.class_name} lookup #{mail[header.to_sym]}"
item = assoc.class_name.constantize
assoc_object = nil
assoc_has_object = false
if item.respond_to?(:name)
assoc_has_object = true
if item.lookup(name: mail[ header.to_sym ])
assoc_object = item.lookup(name: mail[ header.to_sym ])
if item.lookup(name: mail[header.to_sym])
assoc_object = item.lookup(name: mail[header.to_sym])
end
elsif item.respond_to?(:login)
assoc_has_object = true
if item.lookup(login: mail[ header.to_sym ])
assoc_object = item.lookup(login: mail[ header.to_sym ])
if item.lookup(login: mail[header.to_sym])
assoc_object = item.lookup(login: mail[header.to_sym])
end
end
@@ -666,9 +666,9 @@ returns
if suffix
header = "x-zammad-#{header_name}-#{suffix}-#{key}"
end
if mail[ header.to_sym ]
Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}"
item_object[key] = mail[ header.to_sym ]
if mail[header.to_sym]
Rails.logger.info "header #{header} found #{mail[header.to_sym]}"
item_object[key] = mail[header.to_sym]
end
}
end

View File

@@ -14,7 +14,7 @@ module Channel::Filter::FollowUpCheck
return true
end
setting = Setting.get('postmaster_follow_up_search_in')
setting = Setting.get('postmaster_follow_up_search_in') || []
# get ticket# from body
if setting.include?('body')
@@ -39,7 +39,7 @@ module Channel::Filter::FollowUpCheck
end
# get ticket# from references
if setting.include?('references') || mail[ 'x-zammad-is-auto-response'.to_sym ] == true
if setting.include?('references') || (mail[ 'x-zammad-is-auto-response'.to_sym ] == true || Setting.get('ticket_hook_position') == 'none')
# get all references 'References' + 'In-Reply-To'
references = ''
@@ -66,7 +66,7 @@ module Channel::Filter::FollowUpCheck
end
# get ticket# from references current email has same subject as inital article
if !mail[:subject].empty?
if mail[:subject].present?
# get all references 'References' + 'In-Reply-To'
references = ''

View File

@@ -1,2 +1,5 @@
class Chat::Message < ApplicationModel
include HtmlSanitized
sanitized_html :content
end

View File

@@ -0,0 +1,46 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module HtmlSanitized
extend ActiveSupport::Concern
included do
before_create :sanitized_html_attributes
before_update :sanitized_html_attributes
end
def sanitized_html_attributes
html_attributes = self.class.instance_variable_get(:@sanitized_html) || []
return if html_attributes.empty?
html_attributes.each do |attribute|
value = send(attribute)
next if value.blank?
next if !sanitizeable?(attribute, value)
send("#{attribute}=".to_sym, HtmlSanitizer.strict(value))
end
end
def sanitizeable?(_attribute, _value)
true
end
# methods defined here are going to extend the class, not the instance of it
class_methods do
=begin
serve methode to mark HTML attrbibutes that need to get sanitized
class Model < ApplicationModel
include Sanitized
sanitized_html :body
end
=end
def sanitized_html(*attributes)
@sanitized_html = attributes
end
end
end

View File

@@ -1,5 +1,7 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class Ticket::Article < ApplicationModel
include HtmlSanitized
load 'ticket/article/assets.rb'
include Ticket::Article::Assets
load 'ticket/article/history_log.rb'
@@ -16,6 +18,8 @@ class Ticket::Article < ApplicationModel
before_create :check_subject, :check_message_id_md5
before_update :check_subject, :check_message_id_md5
sanitized_html :body
notify_clients_support
activity_stream_support(
@@ -212,6 +216,12 @@ returns:
)
end
def sanitizeable?(attribute, _value)
return true if attribute != :body
return false if content_type.blank?
content_type =~ /html/i
end
private
# strip not wanted chars

View File

@@ -0,0 +1,48 @@
# content of this tags will also be removed
Rails.application.config.html_sanitizer_tags_remove_content = %w(
style
)
# only this tags are allowed
Rails.application.config.html_sanitizer_tags_whitelist = %w(
a abbr acronym address area article aside audio
b bdi bdo big blockquote br
canvas caption center cite code col colgroup command
datalist dd del details dfn dir div dl dt em
figcaption figure footer h1 h2 h3 h4 h5 h6 header hr
i img ins kbd label legend li map mark menu meter nav
ol output optgroup option p pre q
s samp section small span strike strong sub summary sup
text table tbody td tfoot th thead time tr tt u ul var video
)
# attributes allowed for tags
Rails.application.config.html_sanitizer_attributes_whitelist = {
:all => %w(class dir lang style title translate data-signature data-signature-id),
'a' => %w(href hreflang name rel),
'abbr' => %w(title),
'blockquote' => %w(cite),
'col' => %w(span width),
'colgroup' => %w(span width),
'data' => %w(value),
'del' => %w(cite datetime),
'dfn' => %w(title),
'img' => %w(align alt border height src srcset width),
'ins' => %w(cite datetime),
'li' => %w(value),
'ol' => %w(reversed start type),
'table' => %w(align bgcolor border cellpadding cellspacing frame rules sortable summary width),
'td' => %w(abbr align axis colspan headers rowspan valign width),
'th' => %w(abbr align axis colspan headers rowspan scope sorted valign width),
'ul' => %w(type),
'q' => %w(cite),
'time' => %w(datetime pubdate),
}
# only this css properties are allowed
Rails.application.config.html_sanitizer_css_properties_whitelist = %w(
width height
max-width min-width
max-height min-height
)

View File

@@ -16,10 +16,10 @@ class CreateBase < ActiveRecord::Migration
create_table :users do |t|
t.references :organization, null: true
t.string :login, limit: 100, null: false
t.string :login, limit: 255, null: false
t.string :firstname, limit: 100, null: true, default: ''
t.string :lastname, limit: 100, null: true, default: ''
t.string :email, limit: 140, null: true, default: ''
t.string :email, limit: 255, null: true, default: ''
t.string :image, limit: 100, null: true
t.string :image_source, limit: 200, null: true
t.string :web, limit: 100, null: true, default: ''

View File

@@ -0,0 +1,52 @@
class UnableToEnableTimeAccounting633 < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
Setting.create_if_not_exists(
title: 'Time Accounting',
name: 'time_accounting',
area: 'Web::Base',
description: 'Enable time accounting.',
options: {
form: [
{
display: '',
null: true,
name: 'time_accounting',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
preferences: {
authentication: true,
permission: ['admin.time_accounting'],
},
state: false,
frontend: true
)
Setting.create_if_not_exists(
title: 'Time Accounting Selector',
name: 'time_accounting_selector',
area: 'Web::Base',
description: 'Enable time accounting for this tickets.',
options: {
form: [
{},
],
},
preferences: {
authentication: true,
permission: ['admin.time_accounting'],
},
state: {},
frontend: true
)
end
end

View File

@@ -0,0 +1,10 @@
class LoginEmailLength650 < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
change_column(:users, :login, :string, limit: 255)
change_column(:users, :email, :string, limit: 255)
end
end

92
lib/html_sanitizer.rb Normal file
View File

@@ -0,0 +1,92 @@
class HtmlSanitizer
def self.strict(string)
# config
tags_remove_content = Rails.configuration.html_sanitizer_tags_remove_content
tags_whitelist = Rails.configuration.html_sanitizer_tags_whitelist
attributes_whitelist = Rails.configuration.html_sanitizer_attributes_whitelist
css_properties_whitelist = Rails.configuration.html_sanitizer_css_properties_whitelist
scrubber = Loofah::Scrubber.new do |node|
# remove tags with subtree
if tags_remove_content.include?(node.name)
node.remove
end
# replace tags, keep subtree
if !tags_whitelist.include?(node.name)
traversal(node, scrubber)
end
# prepare src attribute
if node['src']
src = cleanup(node['src'])
if src =~ /(javascript|livescript|vbscript):/i || src.start_with?('http', 'ftp', '//')
traversal(node, scrubber)
end
end
# clean style / only use allowed style properties
if node['style']
pears = node['style'].downcase.gsub(/\t|\n|\r/, '').split(';')
style = ''
pears.each { |pear|
prop = pear.split(':')
next if !prop[0]
key = prop[0].strip
next if !css_properties_whitelist.include?(key)
style += "#{pear};"
}
node['style'] = style
if style == ''
node.delete('style')
end
end
# scan for invalid link content
%w(href style).each { |attribute_name|
next if !node[attribute_name]
href = cleanup(node[attribute_name])
next if href !~ /(javascript|livescript|vbscript):/i
node.delete(attribute_name)
}
# remove attributes if not whitelisted
node.each { |attribute, _value|
attribute_name = attribute.downcase
next if attributes_whitelist[:all].include?(attribute_name) || (attributes_whitelist[node.name] && attributes_whitelist[node.name].include?(attribute_name))
node.delete(attribute)
}
# prepare links
if node['href']
href = cleanup(node['href'])
next if !href.start_with?('http', 'ftp', '//')
node.set_attribute('rel', 'nofollow')
node.set_attribute('target', '_blank')
end
end
Loofah.fragment(string).scrub!(scrubber).to_s
end
def self.traversal(node, scrubber)
node.children.each { |child|
if child.class == Nokogiri::XML::CDATA
node.before Nokogiri::XML::Text.new(node.content, node.document)
else
node.before Loofah.fragment(child.to_s).scrub!(scrubber)
end
}
node.remove
end
def self.cleanup(string)
string.downcase.gsub(/[[:space:]]|\t|\n|\r/, '').gsub(%r{/\*.*?\*/}, '').gsub(/<!--.*?-->/, '').gsub(/\[.+?\]/, '')
end
private_class_method :traversal
private_class_method :cleanup
end

View File

@@ -32,6 +32,7 @@ returns
next if entry =~ %r{channel/}i
next if entry =~ %r{observer/}i
next if entry =~ %r{store/provider/}i
next if entry =~ %r{models/concerns/}i
entry.gsub!(dir, '')
entry = entry.to_classname
model_class = load_adapter(entry)

View File

@@ -277,9 +277,14 @@ $(function() {
var formData = new FormData(_this.$form[0])
/* unfortunaly not working in safari and some IEs - https://developer.mozilla.org/en-US/docs/Web/API/FormData
if (!formData.has('title')) {
formData.append('title', this.options.messageTitle)
}
*/
if (!_this.$form.find('[name=title]').val()) {
formData.append('title', this.options.messageTitle)
}
if (this.options.test) {
formData.append('test', true)

View File

@@ -28,8 +28,10 @@ if [ "$LEVEL" == '1' ]; then
rm test/browser/agent_ticket_online_notification_test.rb
rm test/browser/agent_ticket_overview_level0_test.rb
rm test/browser/agent_ticket_overview_level1_test.rb
rm test/browser/agent_ticket_overview_tab_test.rb
rm test/browser/agent_ticket_tag_test.rb
rm test/browser/agent_ticket_text_module_test.rb
rm test/browser/agent_ticket_time_accounting_test.rb
rm test/browser/agent_ticket_update1_test.rb
rm test/browser/agent_ticket_update2_test.rb
rm test/browser/agent_ticket_update3_test.rb
@@ -83,8 +85,10 @@ elif [ "$LEVEL" == '2' ]; then
rm test/browser/agent_ticket_online_notification_test.rb
rm test/browser/agent_ticket_overview_level0_test.rb
rm test/browser/agent_ticket_overview_level1_test.rb
rm test/browser/agent_ticket_overview_tab_test.rb
rm test/browser/agent_ticket_tag_test.rb
# test/browser/agent_ticket_text_module_test.rb
# test/browser/agent_ticket_time_accounting_test.rb
# test/browser/agent_ticket_update1_test.rb
# test/browser/agent_ticket_update2_test.rb
# test/browser/agent_ticket_update3_test.rb
@@ -138,8 +142,10 @@ elif [ "$LEVEL" == '3' ]; then
rm test/browser/agent_ticket_online_notification_test.rb
rm test/browser/agent_ticket_overview_level0_test.rb
rm test/browser/agent_ticket_overview_level1_test.rb
rm test/browser/agent_ticket_overview_tab_test.rb
# test/browser/agent_ticket_tag_test.rb
rm test/browser/agent_ticket_text_module_test.rb
rm test/browser/agent_ticket_time_accounting_test.rb
rm test/browser/agent_ticket_update1_test.rb
rm test/browser/agent_ticket_update2_test.rb
rm test/browser/agent_ticket_update3_test.rb
@@ -193,8 +199,10 @@ elif [ "$LEVEL" == '4' ]; then
# test/browser/agent_ticket_online_notification_test.rb
# test/browser/agent_ticket_overview_level0_test.rb
# test/browser/agent_ticket_overview_level1_test.rb
# test/browser/agent_ticket_overview_tab_test.rb
rm test/browser/agent_ticket_tag_test.rb
rm test/browser/agent_ticket_text_module_test.rb
rm test/browser/agent_ticket_time_accounting_test.rb
rm test/browser/agent_ticket_update1_test.rb
rm test/browser/agent_ticket_update2_test.rb
rm test/browser/agent_ticket_update3_test.rb
@@ -247,8 +255,10 @@ elif [ "$LEVEL" == '5' ]; then
rm test/browser/agent_ticket_online_notification_test.rb
rm test/browser/agent_ticket_overview_level0_test.rb
rm test/browser/agent_ticket_overview_level1_test.rb
rm test/browser/agent_ticket_overview_tab_test.rb
rm test/browser/agent_ticket_tag_test.rb
rm test/browser/agent_ticket_text_module_test.rb
rm test/browser/agent_ticket_time_accounting_test.rb
rm test/browser/agent_ticket_update1_test.rb
rm test/browser/agent_ticket_update2_test.rb
rm test/browser/agent_ticket_update3_test.rb
@@ -304,8 +314,10 @@ elif [ "$LEVEL" == '6' ]; then
rm test/browser/agent_ticket_online_notification_test.rb
rm test/browser/agent_ticket_overview_level0_test.rb
rm test/browser/agent_ticket_overview_level1_test.rb
rm test/browser/agent_ticket_overview_tab_test.rb
rm test/browser/agent_ticket_tag_test.rb
rm test/browser/agent_ticket_text_module_test.rb
rm test/browser/agent_ticket_time_accounting_test.rb
rm test/browser/agent_ticket_update1_test.rb
rm test/browser/agent_ticket_update2_test.rb
rm test/browser/agent_ticket_update3_test.rb

View File

@@ -0,0 +1,87 @@
# encoding: utf-8
require 'browser_test_helper'
class AgentTicketOverviewTabTest < TestCase
def test_i
@browser = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
title = "test #{rand(9_999_999)}"
# create new ticket
ticket1 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: "overview tab test #1 - #{title}",
body: "overview tab test #1 - #{title}",
}
)
ticket2 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: "overview tab test #2 - #{title}",
body: "overview tab test #2 - #{title}",
}
)
ticket3 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: "overview tab test #3 - #{title}",
body: "overview tab test #3 - #{title}",
}
)
tasks_close_all()
#click(text: 'Overviews')
# enable full overviews
#execute(
# js: '$(".content.active .sidebar").css("display", "block")',
#)
#click(text: 'Unassigned & Open')
sleep 8 # till overview is rendered
ticket_open_by_overview(
number: ticket1[:number],
title: "overview tab test #1 - #{title}",
link: '#ticket/view/all_unassigned',
)
assert_equal(1, @browser.find_elements(css: '.tasks .task').count)
ticket_update(
data: {
body: 'some body',
state: 'closed',
},
task_type: 'closeNextInOverview', # default: stayOnTab / possible: closeTab, closeNextInOverview, stayOnTab
)
match(
css: '.tasks .task.is-active',
value: "overview tab test #2 - #{title}",
)
assert_equal(1, @browser.find_elements(css: '.tasks .task').count)
ticket_update(
data: {
body: 'some body',
state: 'closed',
},
task_type: 'closeTab', # default: stayOnTab / possible: closeTab, closeNextInOverview, stayOnTab
)
assert_equal(0, @browser.find_elements(css: '.tasks .task').count)
# cleanup
tasks_close_all()
end
end

View File

@@ -0,0 +1,156 @@
# encoding: utf-8
require 'browser_test_helper'
class AgentTicketTimeAccountingTest < TestCase
def test_macro
@browser = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
# enable time accounting
click(
css: 'a[href="#manage"]',
)
click(
css: '.content.active a[href="#manage/time_accounting"]',
)
switch(
css: '.content.active .js-timeAccountingSetting',
type: 'on',
)
ticket1 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'some subject - time accounting#1',
body: 'some body - time accounting#1',
},
)
ticket_update(
data: {
body: 'some note',
},
do_not_submit: true,
)
click(
css: '.active .js-submit',
)
watch_for(
css: '.content.active .modal',
value: 'Time Accounting',
)
set(
css: '.content.active .modal [name=time_unit]',
value: '4',
)
click(
css: '.content.active .modal .js-submit',
)
watch_for_disappear(
css: '.content.active .modal',
value: 'Time Accounting',
)
watch_for(
css: '.content.active .js-timeUnit',
value: '4',
)
ticket2 = ticket_create(
data: {
customer: 'nico',
group: 'Users',
title: 'some subject - time accounting#2',
body: 'some body - time accounting#2',
},
)
ticket_update(
data: {
body: 'some note',
},
do_not_submit: true,
)
click(
css: '.active .js-submit',
)
watch_for(
css: '.content.active .modal',
value: 'Time Accounting',
)
set(
css: '.content.active .modal [name=time_unit]',
value: '4,6',
)
click(
css: '.content.active .modal .js-submit',
)
watch_for_disappear(
css: '.content.active .modal',
value: 'Time Accounting',
)
watch_for(
css: '.content.active .js-timeUnit',
value: '4.6',
)
ticket_update(
data: {
body: 'some note2',
},
do_not_submit: true,
)
click(
css: '.active .js-submit',
)
watch_for(
css: '.content.active .modal',
value: 'Time Accounting',
)
set(
css: '.content.active .modal [name=time_unit]',
value: '4abc',
)
click(
css: '.content.active .modal .js-submit',
)
watch_for(
css: '.content.active .modal [name=time_unit].has-error',
)
set(
css: '.content.active .modal [name=time_unit]',
value: '4 ',
)
click(
css: '.content.active .modal .js-submit',
)
watch_for_disappear(
css: '.content.active .modal',
value: 'Time Accounting',
)
watch_for(
css: '.content.active .js-timeUnit',
value: '8.6',
)
# disable time accounting
click(
css: 'a[href="#manage"]',
)
click(
css: '.content.active a[href="#manage/time_accounting"]',
)
switch(
css: '.content.active .js-timeAccountingSetting',
type: 'off',
)
end
end

View File

@@ -963,6 +963,8 @@ class TestCase < Test::Unit::TestCase
=begin
set type of task (closeTab, closeNextInOverview, stayOnTab)
task_type(
browser: browser1,
type: 'stayOnTab',
@@ -975,7 +977,6 @@ class TestCase < Test::Unit::TestCase
log('task_type', params)
instance = params[:browser] || @browser
if params[:type]
instance.find_elements(css: '.content.active .js-secondaryActionButtonLabel')[0].click
instance.find_elements(css: ".content.active .js-secondaryActionLabel[data-type=#{params[:type]}]")[0].click
@@ -2032,6 +2033,7 @@ wait untill text in selector disabppears
key1: 'some value',
},
do_not_submit: true,
task_type: 'stayOnTab', # default: stayOnTab / possible: closeTab, closeNextInOverview, stayOnTab
)
=end
@@ -2203,14 +2205,13 @@ wait untill text in selector disabppears
}
if !found
screenshot(browser: instance, comment: 'ticket_update_discard_message_failed')
raise 'no discard message found'
end
end
task_type(
browser: instance,
type: 'stayOnTab',
type: params[:task_type] || 'stayOnTab',
)
if params[:do_not_submit]
@@ -2220,6 +2221,13 @@ wait untill text in selector disabppears
instance.find_elements(css: '.content.active .js-submit')[0].click
# do not stay on tab
if params[:task_type] == 'closeTab' || params[:task_type] == 'closeNextInOverview'
sleep 1
screenshot(browser: instance, comment: 'ticket_update')
return
end
9.times {
begin
text = instance.find_elements(css: '.content.active .js-reset')[0].text
@@ -2316,6 +2324,13 @@ wait untill text in selector disabppears
link: "#ticket/view/#{name}",
)
ticket_open_by_overview(
browser: browser2,
number: ticket1[:number],
text: title,
link: "#ticket/view/#{name}",
)
=end
def ticket_open_by_overview(params)
@@ -2338,12 +2353,25 @@ wait untill text in selector disabppears
js: '$(".content.active .sidebar").css("display", "none")',
)
screenshot(browser: instance, comment: 'ticket_open_by_overview_search')
instance.find_elements(partial_link_text: params[:number])[0].click
if params[:title]
element = instance.find_elements(partial_link_text: params[:title])[0]
if !element
screenshot(browser: instance, comment: 'ticket_open_by_overview_no_ticket_failed')
raise "unable to find ticket #{params[:title]} in overview #{params[:link]}!"
end
else
element = instance.find_elements(partial_link_text: params[:number])[0]
if !element
screenshot(browser: instance, comment: 'ticket_open_by_overview_no_ticket_failed')
raise "unable to find ticket #{params[:number]} in overview #{params[:link]}!"
end
end
element.click
sleep 1
number = instance.find_elements(css: '.content.active .ticketZoom-header .ticket-number')[0].text
if number !~ /#{params[:number]}/
screenshot(browser: instance, comment: 'ticket_open_by_overview_failed')
raise "unable to search/find ticket #{params[:number]}!"
screenshot(browser: instance, comment: 'ticket_open_by_overview_open_failed_failed')
raise "unable to open ticket #{params[:number]}!"
end
sleep 1
assert(true, "ticket #{params[:number]} found")

View File

@@ -104,6 +104,50 @@ no reference "
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', nil)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
assert_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_not_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', 'references')
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_subject)
assert_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_body)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_attachment)
assert_not_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references1)
assert_equal(ticket.id, ticket_p.id)
travel 1.second
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string_references2)
assert_equal(ticket.id, ticket_p.id)
Setting.set('postmaster_follow_up_search_in', setting_orig)
travel 1.second
@@ -325,4 +369,41 @@ Some Text"
Setting.set('postmaster_follow_up_search_in', setting_orig)
end
test 'process with follow up check - with none ticket# in subject' do
setting_orig = Setting.get('postmaster_follow_up_search_in')
Setting.set('postmaster_follow_up_search_in', [])
ticket_hook_position_orig = Setting.get('ticket_hook_position')
Setting.set('ticket_hook_position', 'none')
subject = 'some title'
email_raw_string = "From: me@example.com
To: bob@example.com
Subject: #{subject}
Message-ID: <123456789-follow-up-test-ticket_hook_position-none@linuxhotel.de>
Some Text"
ticket_p1, article_1, user_1, mail = Channel::EmailParser.new.process({}, email_raw_string)
ticket1 = Ticket.find(ticket_p1.id)
assert_equal(subject, ticket1.title)
# follow up possible because same subject
subject = 'new subject lalala'
email_raw_string = "From: bob@example.com
To: me@example.com
Subject: AW: #{subject}
Message-ID: <123456789-follow-up-test-ticket_hook_position-none-2@linuxhotel.de>
References: <123456789-follow-up-test-ticket_hook_position-none@linuxhotel.de>
Some Text"
ticket_p2, article_p2, user_p2, mail = Channel::EmailParser.new.process({}, email_raw_string)
ticket2 = Ticket.find(ticket_p2.id)
assert_equal(ticket1.id, ticket2.id)
Setting.set('ticket_hook_position', ticket_hook_position_orig)
Setting.set('postmaster_follow_up_search_in', setting_orig)
end
end

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,74 @@
# encoding: utf-8
require 'test_helper'
class HtmlSanitizerTest < ActiveSupport::TestCase
test 'xss' do
assert_equal(HtmlSanitizer.strict('<b>123</b>'), '<b>123</b>')
assert_equal(HtmlSanitizer.strict('<script><b>123</b></script>'), '&lt;b&gt;123&lt;/b&gt;')
assert_equal(HtmlSanitizer.strict('<script><style><b>123</b></style></script>'), '&lt;style&gt;&lt;b&gt;123&lt;/b&gt;&lt;/style&gt;')
assert_equal(HtmlSanitizer.strict('<abc><i><b>123</b><bbb>123</bbb></i></abc>'), '<i><b>123</b>123</i>')
assert_equal(HtmlSanitizer.strict('<abc><i><b>123</b><bbb>123<i><ccc>abc</ccc></i></bbb></i></abc>'), '<i><b>123</b>123<i>abc</i></i>')
assert_equal(HtmlSanitizer.strict('<not_existing>123</not_existing>'), '123')
assert_equal(HtmlSanitizer.strict('<script type="text/javascript">alert("XSS!");</script>'), 'alert("XSS!");')
assert_equal(HtmlSanitizer.strict('<SCRIPT SRC=http://xss.rocks/xss.js></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=javascript:alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=JaVaScRiPt:alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=`javascript:alert("RSnake says, \'XSS\'")`>'), '')
assert_equal(HtmlSanitizer.strict('<IMG """><SCRIPT>alert("XSS")</SCRIPT>">'), '<img>alert("XSS")"&gt;')
assert_equal(HtmlSanitizer.strict('<IMG SRC=# onmouseover="alert(\'xxs\')">'), '<img src="#">')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav&#x09;ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav&#x0A;ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="jav&#x0D;ascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC=" &#14; javascript:alert(\'XSS\');">'), '<img src="">')
assert_equal(HtmlSanitizer.strict('<SCRIPT/XSS SRC="http://xss.rocks/xss.js"></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>'), '')
assert_equal(HtmlSanitizer.strict('<SCRIPT/SRC="http://xss.rocks/xss.js"></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<<SCRIPT>alert("XSS");//<</SCRIPT>'), '&lt;alert("XSS");//&lt;')
assert_equal(HtmlSanitizer.strict('<SCRIPT SRC=http://xss.rocks/xss.js?< B >'), '')
assert_equal(HtmlSanitizer.strict('<SCRIPT SRC=//xss.rocks/.j>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javascript:alert(\'XSS\')"'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javascript:alert(\'XSS\')" abc<b>123</b>'), '123')
assert_equal(HtmlSanitizer.strict('<iframe src=http://xss.rocks/scriptlet.html <'), '')
assert_equal(HtmlSanitizer.strict('</script><script>alert(\'XSS\');</script>'), 'alert(\'XSS\');')
assert_equal(HtmlSanitizer.strict('<STYLE>li {list-style-image: url("javascript:alert(\'XSS\')");}</STYLE><UL><LI>XSS</br>'), '<ul><li>XSS</li></ul>')
assert_equal(HtmlSanitizer.strict('<IMG SRC=\'vbscript:msgbox("XSS")\'>'), '')
assert_equal(HtmlSanitizer.strict('<IMG SRC="livescript:[code]">'), '')
assert_equal(HtmlSanitizer.strict('<svg/onload=alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<BODY ONLOAD=alert(\'XSS\')>'), '')
assert_equal(HtmlSanitizer.strict('<LINK REL="stylesheet" HREF="javascript:alert(\'XSS\');">'), '')
assert_equal(HtmlSanitizer.strict('<STYLE>@import\'http://xss.rocks/xss.css\';</STYLE>'), '')
assert_equal(HtmlSanitizer.strict('<META HTTP-EQUIV="Link" Content="<http://xss.rocks/xss.css>; REL=stylesheet">'), '')
assert_equal(HtmlSanitizer.strict('<IMG STYLE="java/*XSS*/script:(alert(\'XSS\'), \'\')">'), '<img>')
assert_equal(HtmlSanitizer.strict('<IMG src="java/*XSS*/script:(alert(\'XSS\'), \'\')">'), '')
assert_equal(HtmlSanitizer.strict('<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>'), '')
assert_equal(HtmlSanitizer.strict('<TABLE><TD BACKGROUND="javascript:alert(\'XSS\')">'), '<table><td></td></table>')
assert_equal(HtmlSanitizer.strict('<DIV STYLE="background-image: url(javascript:alert(\'XSS\'), \'\')">'), '<div></div>')
assert_equal(HtmlSanitizer.strict('<a href="/some/path">test</a>'), '<a href="/some/path">test</a>')
assert_equal(HtmlSanitizer.strict('<a href="https://some/path">test</a>'), '<a href="https://some/path" rel="nofollow" target="_blank">test</a>')
assert_equal(HtmlSanitizer.strict('<XML ID="xss"><I><B><IMG SRC="javas<!-- -->cript:alert(\'XSS\')"></B></I></XML>'), '<i><b></b></i>')
assert_equal(HtmlSanitizer.strict('<IMG SRC="javas<!-- -->cript:alert(\'XSS\')">'), '')
assert_equal(HtmlSanitizer.strict(' <HEAD><META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=UTF-7"> </HEAD>+ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-'), ' +ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-')
assert_equal(HtmlSanitizer.strict('<SCRIPT a=">" SRC="httx://xss.rocks/xss.js"></SCRIPT>'), '')
assert_equal(HtmlSanitizer.strict('<A HREF="h
tt p://6 6.000146.0x7.147/">XSS</A>'), '<a href="h%0Att%20%20p://6%206.000146.0x7.147/" rel="nofollow" target="_blank">XSS</a>')
assert_equal(HtmlSanitizer.strict('<A HREF="//www.google.com/">XSS</A>'), '<a href="//www.google.com/" rel="nofollow" target="_blank">XSS</a>')
assert_equal(HtmlSanitizer.strict('<form id="test"></form><button form="test" formaction="javascript:alert(1)">X</button>'), 'X')
assert_equal(HtmlSanitizer.strict('<maction actiontype="statusline#http://google.com" xlink:href="javascript:alert(2)">CLICKME</maction>'), 'CLICKME')
assert_equal(HtmlSanitizer.strict('<a xlink:href="javascript:alert(2)">CLICKME</a>'), '<a>CLICKME</a>')
assert_equal(HtmlSanitizer.strict('<!--<img src="--><img src=x onerror=alert(1)//">'), '<img src="x">')
assert_equal(HtmlSanitizer.strict('<![><img src="]><img src=x onerror=alert(1)//">'), '<img src="%5D&gt;&lt;img%20src=x%20onerror=alert(1)//">')
assert_equal(HtmlSanitizer.strict('<svg><![CDATA[><image xlink:href="]]><img src=xx:x onerror=alert(2)//"></svg>'), '')
assert_equal(HtmlSanitizer.strict('<abc><img src="</abc><img src=x onerror=alert(1)//">'), '<img src="&lt;/abc&gt;&lt;img%20src=x%20onerror=alert(1)//">')
assert_equal(HtmlSanitizer.strict('<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>'), '')
assert_equal(HtmlSanitizer.strict('<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></embed>'), '')
assert_equal(HtmlSanitizer.strict('<img[a][b]src=x[d]onerror[c]=[e]"alert(1)">'), '<img>')
assert_equal(HtmlSanitizer.strict('<a href="[a]java[b]script[c]:alert(1)">XXX</a>'), '<a>XXX</a>')
assert_equal(HtmlSanitizer.strict('<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>'), 'alert(1)')
assert_equal(HtmlSanitizer.strict('<a style="position:fixed;top:0;left:0;width: 260px;height:100vh;background-color:red;display: block;" href="http://example.com"></a>'), '<a style="width: 260px;height:100vh;" href="http://example.com" rel="nofollow" target="_blank"></a>')
end
end

View File

@@ -0,0 +1,185 @@
# encoding: utf-8
require 'test_helper'
class TicketXssTest < ActiveSupport::TestCase
test 'xss via model' do
ticket = Ticket.create(
title: 'test 123 <script type="text/javascript">alert("XSS!");</script>',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
assert(ticket, 'ticket created')
assert_equal('test 123 <script type="text/javascript">alert("XSS!");</script>', ticket.title, 'ticket.title verify')
assert_equal('Users', ticket.group.name, 'ticket.group verify')
assert_equal('new', ticket.state.name, 'ticket.state verify')
article1 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: '<script type="text/javascript">alert("XSS!");</script>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal('alert("XSS!");', article1.body, 'article1.body verify - inbound')
article2 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'please tell me this doesn\'t work: <script type="text/javascript">alert("XSS!");</script>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal('please tell me this doesn\'t work: alert("XSS!");', article2.body, 'article2.body verify - inbound')
article3 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'please tell me this doesn\'t work: <table>ada<tr></tr></table><div class="adasd" id="123" data-abc="123"></div><div><a href="javascript:someFunction()">LINK</a><a href="http://lalal.de">aa</a><some_not_existing>ABC</some_not_existing>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal("please tell me this doesn't work: <table>ada<tr></tr>
</table><div class=\"adasd\"></div><div>
<a>LINK</a><a href=\"http://lalal.de\" rel=\"nofollow\" target=\"_blank\">aa</a>ABC</div>", article3.body, 'article3.body verify - inbound')
article4 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'please tell me this doesn\'t work: <video>some video</video><foo>alal</foo>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal("please tell me this doesn't work: <video>some video</video>alal", article4.body, 'article4.body verify - inbound')
article5 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/plain',
body: 'please tell me this doesn\'t work: <table>ada<tr></tr></table><div class="adasd" id="123" data-signature-id="123"></div><div><a href="javascript:someFunction()">LINK</a><a href="http://lalal.de">aa</a><some_not_existing>ABC</some_not_existing>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal('please tell me this doesn\'t work: <table>ada<tr></tr></table><div class="adasd" id="123" data-signature-id="123"></div><div><a href="javascript:someFunction()">LINK</a><a href="http://lalal.de">aa</a><some_not_existing>ABC</some_not_existing>', article5.body, 'article5.body verify - inbound')
article6 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'some message article helper test1 <div><img style="width: 85.5px; height: 49.5px" src="cid:15.274327094.140938@zammad.example.com">asdasd<img src="cid:15.274327094.140939@zammad.example.com"><br>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal('some message article helper test1 <div>
<img style="width: 85.5px; height: 49.5px;" src="cid:15.274327094.140938@zammad.example.com">asdasd<img src="cid:15.274327094.140939@zammad.example.com"><br>
</div>', article6.body, 'article6.body verify - inbound')
article7 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'some message article helper test1 <div><img style="width: 85.5px; height: 49.5px" src="api/v1/ticket_attachment/123/123/123">asdasd<img src="api/v1/ticket_attachment/123/123/123"><br>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal('some message article helper test1 <div>
<img style="width: 85.5px; height: 49.5px;" src="api/v1/ticket_attachment/123/123/123">asdasd<img src="api/v1/ticket_attachment/123/123/123"><br>
</div>', article7.body, 'article7.body verify - inbound')
article8 = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject <script type="text/javascript">alert("XSS!");</script>',
message_id: 'some@id',
content_type: 'text/html',
body: 'some message article helper test1 <a href="#" onclick="some_function();">abc</a> <a href="https://example.com" oNclIck="some_function();">123</a><body>123</body>',
internal: false,
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
type: Ticket::Article::Type.find_by(name: 'email'),
updated_by_id: 1,
created_by_id: 1,
)
assert_equal('some message article helper test1 <a href="#">abc</a> <a href="https://example.com" rel="nofollow" target="_blank">123</a>123', article8.body, 'article8.body verify - inbound')
end
test 'xss via mail' do
data = 'From: ME Bob <me@example.com>
To: customer@example.com
Subject: some subject
Content-Type: text/html
MIME-Version: 1.0
no HTML <script type="text/javascript">alert(\'XSS\')</script>'
parser = Channel::EmailParser.new
ticket, article, user = parser.process({}, data)
assert_equal('text/html', ticket.articles.first.content_type)
assert_equal('no HTML alert(\'XSS\')', ticket.articles.first.body)
data = 'From: ME Bob <me@example.com>
To: customer@example.com
Subject: some subject
Content-Type: text/plain
MIME-Version: 1.0
no HTML <script type="text/javascript">alert(\'XSS\')</script>'
parser = Channel::EmailParser.new
ticket, article, user = parser.process({}, data)
assert_equal('text/plain', ticket.articles.first.content_type)
assert_equal('no HTML <script type="text/javascript">alert(\'XSS\')</script>', ticket.articles.first.body)
end
end