استبدال القوالب من جهة الخادم
أدرج قيمًا مولَّدة على الخادم (مثل تعبيرات Go html/template بالصيغة `{{ .Field }}`) داخل JavaScript المشوَّش بـ VM دون كسر خط التشويش.
المشكلة
يقدّم خادمك الخلفي (Go أو Rails أو Django أو PHP أو …) ملف JavaScript يجب أن يُصيَّر جزء من محتوياته
مع كل طلب - نقطة نهاية API، أو قائمة أعلام ميزات، أو كتلة حالة ابتدائية، أو معرِّف بناء، أو nonce. أنت تشوّش
الـ JS بـ vmObfuscation: true، لكنك تحتاج أيضًا إلى أن يستبدل محرك القوالب العناصر النائبة في الناتج
المشوَّش بعد انتهاء التشويش. فإذا امتُصّت العناصر النائبة داخل بايت كود VM (وهو الافتراضي للنصوص
والمعرِّفات)، فلن يجد محرك القوالب شيئًا ليستبدله.
أي خيار أختار؟
| قيمة الخادم هي… | استخدم |
|---|---|
| تعبير JS خام (مصفوفة حرفية، أو كائن، أو رقم، أو قيمة منطقية، أو استدعاء دالة) | reservedNames + عنصر نائب معرِّف |
| نص، وأنت تتحكم في محدِّدات القوالب | reservedStrings + عنصر نائب بنص قالب |
نص، ويفرض محرك القوالب محدِّدات بعينها (مثل Go {{ .Field }}) | reservedStrings + عنصر نائب بنص أو بنص قالب |
النمط 1 - reservedNames مع عنصر نائب معرِّف
الأفضل لـ: إدراج تعبيرات JS الخام (مصفوفات، وكائنات، وأرقام، …) دون أي هموم تتعلق بتهريب علامات الاقتباس.
اختر معرِّفًا مميزًا لن يتعارض أبدًا مع كود حقيقي، وأشر إليه مباشرةً، وأدرج نمط regex يطابقه
في reservedNames. وتحت تشويش VM، يُوجَّه المعرِّف عبر مصفوفة التعبيرات المحجوزة
ويظهر حرفيًا في الناتج.
المصدر
(function () {
var initialState = __INITIAL_STATE__;
bootstrap(initialState);
})();
خيارات المشوِّش
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedNames: ['^__INITIAL_STATE__$']
});
بعد التشويش
في مكان ما من الناتج ستجد مصفوفة تعبيرات محجوزة مشابهة لـ:
let _r = [__INITIAL_STATE__, /* …other entries… */];
يبقى الرمز __INITIAL_STATE__ صامدًا أمام إعادة تسمية المعرِّفات ولا يُمتَص داخل بايت كود VM.
الاستبدال من جهة الخادم (مثال Go)
tpl := template.Must(template.New("obf.js").Parse(obfuscated))
tpl.Execute(w, map[string]any{
"InitialState": map[string]any{"user": "alice", "theme": "dark"},
})
…حيث يستبدل قالب Go لديك الرمز بتعبير JS خام:
{{ `__INITIAL_STATE__` }} → {{ .InitialState | toJSON }}
النتيجة:
let _r = [{"user":"alice","theme":"dark"}, /* … */];
لا علامات اقتباس، ولا تهريب - فتُحلّل القيمة المُدرَجة بواسطة محرك JS بوصفها تعبيرًا عاديًا.
النمط 2 - reservedStrings مع عنصر نائب بنص قالب
الأفضل لـ: محركات القوالب بنمط Go / Jinja التي تتطلب محدِّدات مثل {{ .Field }} والتي تصادف أن تكون نص JS
صالحًا عند تغليفها بعلامات اقتباس خلفية.
يقيم العنصر النائب داخل نص قالب بجزء واحد وبلا إقحام. وتحت تشويش VM يُوجَّه هذا عبر مصفوفة التعبيرات المحجوزة، فيحفظ صيغة العلامات الخلفية الخام في الناتج.
المصدر
(function () {
var featureFlags = JSON.parse(`{{.Config.FeatureFlags}}`);
applyFlags(featureFlags);
})();
خيارات المشوِّش
الواجهة
لحجز العنصر النائب {{.Config.FeatureFlags}} من المصدر أعلاه، أضف نمط regex هذا إلى حقل Reserved Strings
- بشرطات مائلة عكسية مفردة. تخزّن الواجهة القيمة حرفيًا، ولذلك فبخلاف كود JS لا تُضاعَف الشرطات المائلة العكسية:
\{\{[^}]+\}\}

واجهة API
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedStrings: ['\\{\\{[^}]+\\}\\}']
});
بعد التشويش
let _r = [`{{.Config.FeatureFlags}}`, /* … */];
يُحفَظ العنصر النائب بايتًا ببايت، بما في ذلك العلامات الخلفية المحيطة به.
الاستبدال من جهة الخادم
استبدل {{.Config.FeatureFlags}} بنص JSON صالح - فسيقع داخل علامات اقتباس خلفية أثناء التشغيل، وهي لا
تتطلب تهريب علامات الاقتباس الداخلية:
flagsJSON, _ := json.Marshal(config.FeatureFlags) // e.g. {"newCheckout":true,"darkMode":false}
out := strings.ReplaceAll(obfuscated, "{{.Config.FeatureFlags}}", string(flagsJSON))
أثناء التشغيل: JSON.parse(`{"newCheckout":true,"darkMode":false}`) - يعمل.
النمط 3 - reservedStrings مع عنصر نائب بنص مقتبس
الأفضل لـ: الكود المصدري الذي يجب أن يبقى بصيغة ES5 (بلا نصوص قوالب)، أو الحالات التي تتوقع فيها الواجهة المحيطة نصًا حرفيًا عاديًا.
العنصر النائب نص حرفي بعلامات اقتباس مفردة أو مزدوجة. وتحت تشويش VM يُوجَّه عبر
مصفوفة النصوص المحجوزة، التي تُنتَج بوصفها مصفوفة JS مُسلسَلة بـ JSON.stringify - دائمًا بعلامات اقتباس
مزدوجة بصرف النظر عن نمط الاقتباس في المُدخل.
المصدر
(function () {
var tags = JSON.parse("{{.Page.Tags}}");
renderTags(tags);
})();
خيارات المشوِّش
الواجهة
لحجز العنصر النائب {{.Page.Tags}} من المصدر أعلاه، أضف نمط regex هذا إلى حقل Reserved Strings -
بشرطات مائلة عكسية مفردة. تخزّن الواجهة القيمة حرفيًا، ولذلك فبخلاف كود JS لا تُضاعَف الشرطات المائلة العكسية:
\{\{[^}]+\}\}

واجهة API
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedStrings: ['\\{\\{[^}]+\\}\\}']
});
بعد التشويش
var _rs = ["{{.Page.Tags}}", /* … */];
الاستبدال من جهة الخادم
لأن العنصر النائب يقع داخل نص JS بعلامات اقتباس مزدوجة، يجب تهريب علامات "
الداخلية في حمولة JSON المُدرَجة بـ \":
raw, _ := json.Marshal(page.Tags) // e.g. ["news","tech","release"]
// Escape " for embedding inside a JS double-quoted string.
escaped := strings.ReplaceAll(string(raw), `"`, `\"`)
out := strings.ReplaceAll(obfuscated, "{{.Page.Tags}}", escaped)
الناتج:
var _rs = ["[\"news\",\"tech\",\"release\"]", /* … */];
أثناء التشغيل: JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"].
إذا نسيت التهريب، فسيرى المتصفح علامات اقتباس غير متوازنة ويطلق SyntaxError. ويتجنب النمط 2 هذا
تمامًا باستخدام العلامات الخلفية.
عناصر نائبة متعددة في برنامج واحد
تتركّب الأنماط الثلاثة جميعها. فيمكن لنمط reservedStrings واحد مع بديل أن يطابق كل شكل عنصر نائب يصدره
محرك قوالبك:
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedNames: ['^__INITIAL_STATE__$'],
reservedStrings: ['\\{\\{[^}]+\\}\\}']
});
يمكنك مزج العناصر النائبة المعرِّفة (لقيم JS الخام) والعناصر النائبة النصية (لـ JSON المصيَّر) في المصدر نفسه - اختر لكل عنصر نائب بناءً على ما سيُدرجه الخادم فعليًا.
ملاحظات التوافق
- لا تعِد استخدام العناصر النائبة عبر المعرِّفات والنصوص. فالاسم المحجوز مثل
__TOKEN__والنص المحجوز الذي يطابق__TOKEN__يصفان مسارَي كود مختلفين (مصفوفة التعبيرات المحجوزة مقابل مصفوفة النصوص المحجوزة). استخدم أشكالًا نصية مميزة لكل منهما - مثلًا اصطلاحUPPER_SNAKEللعناصر النائبة المعرِّفة، وشكلًا مغلَّفًا بمحدِّدات ({{ ... }}أو%{...}أو<<<...>>>) للعناصر النائبة النصية. وبذلك لا يمكن لخطأ في أحد النمطين أن يطابق الآخر بصمت. - تعمل أنماط
reservedStringsعلى القيم النصية الخام. يُطابَق النمط مع القيمة الفعلية للنص أثناء التشغيل، لا مع النص المصدري. فـ\{\{[^}]+\}\}يطابق النصوص التي تحتوي{{.something}}(أو أي شكل{{...}}آخر). وإذا كان عنصرك النائب قد يُغلَّف بمحتوى إضافي ("prefix-{{.Field}}-suffix")، فسيبقى النمط مطابقًا لكن النص كله يُحفَظ - فخطّط لاستبدالك وفقًا لذلك. - يعمل مع أي محرك قوالب. فرغم أن الأمثلة تستخدم صيغة Go
text/template، لا شيء في تكامل javascript-obfuscator خاص بـ Go. فأي شيء يستطيع إجراء استبدال على مستوى النص في ناتج المشوِّش سينجح: Rails ERB أو Django أو وسوم PHP القصيرة أو sed في خط CI، وغيرها. اختر محدِّدات يصدرها محركك طبيعيًا ولا تتعارض مع صيغة JS الحقيقية.
