105 lines
3.4 KiB
PHP
105 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services\KnowledgeBase;
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class PromptGuardService
|
|
{
|
|
// Soalan yang terlalu pendek atau terlalu panjang
|
|
private const MIN_LENGTH = 3;
|
|
private const MAX_LENGTH = 1000;
|
|
|
|
private array $patterns = [
|
|
// Inject arahan baru
|
|
'instruction_override' => [
|
|
'/ignore (all |previous |above |prior |your |the )?instructions?/i',
|
|
'/forget (everything|all|what you|your instructions|your rules)/i',
|
|
'/override (your |all |previous )?instructions?/i',
|
|
'/disregard (all |your |previous )?instructions?/i',
|
|
'/new instruction[s]?:/i',
|
|
],
|
|
|
|
// Ubah persona / roleplay
|
|
'persona_switch' => [
|
|
'/you are now\b/i',
|
|
'/pretend (you are|to be|that you)/i',
|
|
'/act as (a |an |if |though )/i',
|
|
'/roleplay as/i',
|
|
'/from now on (you are|you will|act|behave)/i',
|
|
'/your (new |true |real )?role is/i',
|
|
],
|
|
|
|
// Jailbreak klasik
|
|
'known_jailbreak' => [
|
|
'/\bDAN\b/', // Do Anything Now
|
|
'/do anything now/i',
|
|
'/jailbreak/i',
|
|
'/developer mode/i',
|
|
'/grandma (trick|exploit|jailbreak)/i',
|
|
'/token manipulation/i',
|
|
],
|
|
|
|
// Cuba dedah system prompt
|
|
'prompt_extraction' => [
|
|
'/what (is|are) your (system |initial |original )?(prompt|instruction)/i',
|
|
'/show (me |us )?(your )?(system |hidden |secret |original )?prompt/i',
|
|
'/repeat (your |the )?(system |above |initial )?prompt/i',
|
|
'/print (your |the )?(system |above |initial )?instructions?/i',
|
|
'/reveal (your |the )?(system |hidden |secret )?prompt/i',
|
|
],
|
|
|
|
// Token injection
|
|
'token_injection' => [
|
|
'/<\|im_start\|>/i',
|
|
'/<\|im_end\|>/i',
|
|
'/\[INST\]/i',
|
|
'/\[\/INST\]/i',
|
|
'/<<SYS>>/i',
|
|
'/\[SYSTEM\]/i',
|
|
'/###\s*System/i',
|
|
'/###\s*Human/i',
|
|
'/###\s*Assistant/i',
|
|
],
|
|
];
|
|
|
|
/**
|
|
* Semak input user. Return array dengan status dan sebab.
|
|
*
|
|
* @return array{ blocked: bool, reason: string|null, category: string|null }
|
|
*/
|
|
public function check(string $input): array
|
|
{
|
|
$input = trim($input);
|
|
|
|
if (mb_strlen($input) < self::MIN_LENGTH) {
|
|
return $this->blocked('Soalan terlalu pendek.', 'too_short');
|
|
}
|
|
|
|
if (mb_strlen($input) > self::MAX_LENGTH) {
|
|
return $this->blocked('Soalan melebihi had aksara.', 'too_long');
|
|
}
|
|
|
|
foreach ($this->patterns as $category => $categoryPatterns) {
|
|
foreach ($categoryPatterns as $pattern) {
|
|
if (preg_match($pattern, $input)) {
|
|
Log::warning('PromptGuard: input blocked', [
|
|
'category' => $category,
|
|
'pattern' => $pattern,
|
|
'input' => mb_substr($input, 0, 200),
|
|
]);
|
|
|
|
return $this->blocked('Input tidak dibenarkan.', $category);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ['blocked' => false, 'reason' => null, 'category' => null];
|
|
}
|
|
|
|
private function blocked(string $reason, string $category): array
|
|
{
|
|
return ['blocked' => true, 'reason' => $reason, 'category' => $category];
|
|
}
|
|
}
|