diff --git a/.github/unicode-test.bin b/.github/unicode-test.bin
new file mode 100644
index 000000000..8e508f05b
--- /dev/null
+++ b/.github/unicode-test.bin
@@ -0,0 +1,24 @@
+Normal line.
+
+This line contains a NO-BREAK SPACE between Hello and World:
+Hello World
+
+This line contains a ZERO WIDTH SPACE between Hello and World:
+HelloWorld
+
+This line contains a ZERO WIDTH NON-JOINER:
+HelloWorld
+
+This line contains a ZERO WIDTH JOINER:
+HelloWorld
+
+This line contains a WORD JOINER:
+HelloWorld
+
+The next line contains a LINE SEPARATOR character:
+Hello
World
+
+The next line contains a PARAGRAPH SEPARATOR character:
+Hello
World
+
+End of file.
diff --git a/docs/Sysconfig-Keys.md b/docs/Sysconfig-Keys.md
index 38c182d39..67e8b877e 100644
--- a/docs/Sysconfig-Keys.md
+++ b/docs/Sysconfig-Keys.md
@@ -49,6 +49,7 @@ These are the system configuration keys used by the platform applications. Any k
* `stream_player_target_help`: Whether or not to produce stream entries for players who activate writeups on a target.
* `log_failed_claims`: Log failed claim attempts?
* `team_encrypted_claims_allowed`: Should we allow claims of flags across teams?
+* `target_metadata_visible`: Whether or not metadata will be visible to normal players. Admins get to see the metadata no matter this settting.
## String and numeric key/val pairs
diff --git a/frontend/modules/target/controllers/WriteupController.php b/frontend/modules/target/controllers/WriteupController.php
index 0ec6cb78b..e6f6db8a2 100644
--- a/frontend/modules/target/controllers/WriteupController.php
+++ b/frontend/modules/target/controllers/WriteupController.php
@@ -109,7 +109,6 @@ public function actionSubmit(int $id)
return $this->redirect(['default/view','id'=>$id]);
}
-
$headshot=Headshot::findOne(['target_id'=>$id,'player_id'=>Yii::$app->user->id]);
if($headshot===null)
{
@@ -140,9 +139,6 @@ public function actionSubmit(int $id)
'model' => $model,
'headshot'=>$headshot,
]);
-
-
-
}
/**
diff --git a/frontend/modules/target/models/Writeup.php b/frontend/modules/target/models/Writeup.php
index c46f28cb4..dc3caa8cf 100644
--- a/frontend/modules/target/models/Writeup.php
+++ b/frontend/modules/target/models/Writeup.php
@@ -63,24 +63,44 @@ public function scenarios()
public function rules()
{
return [
+ [['content'], 'filter', 'filter' => function ($value) {
+ if ($value === null || $value === '') {
+ return null;
+ }
+
+ $value = str_replace("\r\n", "\n", $value);
+ $value = str_replace("\r", "", $value);
+
+ $cleaned = preg_replace('/[\x{2028}\x{2029}]/u', "\n", $value);
+ if ($cleaned !== null && $cleaned !== false) {
+ $value = $cleaned;
+ }
+
+ $value = str_replace("\u{00A0}", ' ', $value);
+
+ $cleaned = preg_replace('/[\x{200B}\x{200C}\x{200D}\x{2060}\x{FEFF}\x{00AD}]/u', '', $value);
+ if ($cleaned !== null && $cleaned !== false) {
+ $value = $cleaned;
+ }
+
+ return $value;
+ }],
+
+ [['approved'], 'default', 'value' => false],
+ ['formatter', 'default', 'value' => 'text'],
+ ['language_id', 'default', 'value' => 'en'],
+ ['status', 'default', 'value' => 'PENDING'],
+
[['player_id', 'target_id','content'], 'required'],
+
[['player_id', 'target_id'], 'integer'],
[['approved'], 'boolean'],
- [['approved'], 'default','value'=>false],
- ['formatter', 'default','value'=>'text'],
- ['language_id', 'default','value'=>'en'],
[['status', 'comment'], 'string'],
- [['content'], 'filter', 'filter' => function ($value) {
- return str_replace(["\r\n", "\r"], "\n", $value);
- }],
[['content'], 'filter','filter'=>'trim'],
- [['content'], 'string','skipOnEmpty'=>false, 'min'=>'20'],
- ['status','default','value'=>'PENDING'],
- [['created_at', 'updated_at'], 'safe'],
+ [['content'], 'string', 'min'=>20],
[['player_id'], 'exist', 'skipOnError' => true, 'targetClass' => Player::class, 'targetAttribute' => ['player_id' => 'id']],
[['target_id'], 'exist', 'skipOnError' => true, 'targetClass' => Target::class, 'targetAttribute' => ['target_id' => 'id']],
[['language_id'], 'exist', 'skipOnError' => true, 'targetClass' => \app\models\Language::class, 'targetAttribute' => ['language_id' => 'id']],
-
];
}
diff --git a/frontend/themes/material/modules/target/views/default/_target_metadata.php b/frontend/themes/material/modules/target/views/default/_target_metadata.php
index 43dd5b127..f4f9f7ed1 100644
--- a/frontend/themes/material/modules/target/views/default/_target_metadata.php
+++ b/frontend/themes/material/modules/target/views/default/_target_metadata.php
@@ -10,9 +10,11 @@
solution)):?>=\Yii::t('app','Solution')?>: formatter->divID = 'markdown-solution'; echo \Yii::$app->formatter->asMarkdown($metadata->solution)?>
+sys->target_metadata_visible || Yii::$app->user->identity->isAdmin):?>
pre_credits)):?>=\Yii::t('app','Pre exploitation credits')?>: formatter->divID = 'markdown-pre-credits'; echo \Yii::$app->formatter->asMarkdown($metadata->pre_credits)?>
pre_exploitation)):?>=\Yii::t('app','Pre exploitation details')?>: formatter->divID = 'markdown-pre-exploitation'; echo \Yii::$app->formatter->asMarkdown($metadata->pre_exploitation)?>
player_id===Yii::$app->user->id && $target->progress==100) || Yii::$app->user->identity->isAdmin) && !empty($metadata->post_exploitation)):?>=\Yii::t('app','Post exploitation')?>: formatter->divID = 'markdown-post-exploitation'; echo \Yii::$app->formatter->asMarkdown($metadata->post_exploitation)?>
player_id===Yii::$app->user->id && $target->progress==100) || Yii::$app->user->identity->isAdmin) && !empty($metadata->post_credits)):?>=\Yii::t('app','Post exploitation credits')?>: formatter->divID = 'markdown-post-credits'; echo \Yii::$app->formatter->asMarkdown($metadata->post_credits)?>
formatter->divID=$oldId; ?>
+