Substituição de templates no servidor
Injete valores renderizados pelo servidor (por exemplo, expressões `{{ .Field }}` do html/template do Go) em JavaScript ofuscado por VM sem quebrar o pipeline de ofuscação.
O problema
Seu backend (Go, Rails, Django, PHP, …) serve um arquivo JavaScript cujo conteúdo precisa ser parcialmente renderizado
a cada requisição - um endpoint de API, uma lista de feature flags, um blob de estado inicial, um id de build, um
nonce. Você ofusca o JS com vmObfuscation: true, mas também precisa que o motor de templates substitua os
placeholders na saída ofuscada depois que a ofuscação termina. Se os placeholders forem absorvidos pelo bytecode
da VM (o padrão para strings e identificadores), o motor de templates não tem nada para substituir.
Qual opção devo escolher?
| O valor do servidor é… | Use |
|---|---|
| Uma expressão JS bruta (literal de array, objeto, número, booleano, chamada de função) | reservedNames + placeholder de identificador |
| Uma string, e você controla os delimitadores do template | reservedStrings + placeholder em template literal |
Uma string, e o motor de templates impõe certos delimitadores (por exemplo, {{ .Field }} do Go) | reservedStrings + placeholder em string ou em template literal |
Padrão 1 - reservedNames com um placeholder de identificador
Ideal para: injetar expressões JS brutas (arrays, objetos, números, …) sem nenhuma preocupação com escape de aspas.
Escolha um identificador distinto que nunca vá colidir com o código real, referencie-o diretamente e adicione em
reservedNames uma regex que corresponda a ele. Sob ofuscação VM, o identificador é roteado pelo array de expressões
reservadas e aparece literalmente na saída.
Código-fonte
(function () {
var initialState = __INITIAL_STATE__;
bootstrap(initialState);
})();
Opções do obfuscator
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedNames: ['^__INITIAL_STATE__$']
});
Depois da ofuscação
Em algum lugar da saída você encontrará um array de expressões reservadas parecido com este:
let _r = [__INITIAL_STATE__, /* …other entries… */];
O token __INITIAL_STATE__ sobrevive à renomeação de identificadores e não é absorvido pelo bytecode da VM.
Substituição no servidor (exemplo em Go)
tpl := template.Must(template.New("obf.js").Parse(obfuscated))
tpl.Execute(w, map[string]any{
"InitialState": map[string]any{"user": "alice", "theme": "dark"},
})
…onde o seu template Go substitui o token por uma expressão JS bruta:
{{ `__INITIAL_STATE__` }} → {{ .InitialState | toJSON }}
Resultado:
let _r = [{"user":"alice","theme":"dark"}, /* … */];
Sem aspas, sem escape - o valor injetado é interpretado pelo motor JS como uma expressão comum.
Padrão 2 - reservedStrings com um placeholder em template literal
Ideal para: motores de templates no estilo Go / Jinja que exigem delimitadores como {{ .Field }}, que por acaso
são texto JS válido quando envolvidos em crases.
O placeholder fica dentro de um template literal com um único quasi e sem interpolação. Sob ofuscação VM, isso é roteado pelo array de expressões reservadas, preservando na saída a forma bruta com crases.
Código-fonte
(function () {
var featureFlags = JSON.parse(`{{.Config.FeatureFlags}}`);
applyFlags(featureFlags);
})();
Opções do obfuscator
UI
Para reservar o placeholder {{.Config.FeatureFlags}} do código-fonte acima, adicione esta regex ao campo Reserved
Strings - com barras invertidas simples. A UI armazena o valor literalmente, então, ao contrário do que acontece em
código JS, as barras invertidas não são duplicadas:
\{\{[^}]+\}\}

API
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedStrings: ['\\{\\{[^}]+\\}\\}']
});
Depois da ofuscação
let _r = [`{{.Config.FeatureFlags}}`, /* … */];
O placeholder é preservado byte a byte, incluindo as crases ao redor.
Substituição no servidor
Substitua {{.Config.FeatureFlags}} por uma string JSON válida - ela ficará entre crases em tempo de execução, o que
não exige escape das aspas internas:
flagsJSON, _ := json.Marshal(config.FeatureFlags) // e.g. {"newCheckout":true,"darkMode":false}
out := strings.ReplaceAll(obfuscated, "{{.Config.FeatureFlags}}", string(flagsJSON))
Em tempo de execução: JSON.parse(`{"newCheckout":true,"darkMode":false}`) - funciona.
Padrão 3 - reservedStrings com um placeholder em string entre aspas
Ideal para: código-fonte que precisa permanecer em ES5 (sem template literals) ou casos em que a API ao redor espera um literal de string comum.
O placeholder é um literal de string entre aspas simples ou duplas. Sob ofuscação VM, ele é roteado pelo array de
strings reservadas, que é emitido como um array JS serializado com JSON.stringify - sempre entre aspas duplas,
independentemente do estilo de aspas da entrada.
Código-fonte
(function () {
var tags = JSON.parse("{{.Page.Tags}}");
renderTags(tags);
})();
Opções do obfuscator
UI
Para reservar o placeholder {{.Page.Tags}} do código-fonte acima, adicione esta regex ao campo Reserved Strings -
com barras invertidas simples. A UI armazena o valor literalmente, então, ao contrário do que acontece em código JS, as
barras invertidas não são duplicadas:
\{\{[^}]+\}\}

API
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedStrings: ['\\{\\{[^}]+\\}\\}']
});
Depois da ofuscação
var _rs = ["{{.Page.Tags}}", /* … */];
Substituição no servidor
Como o placeholder fica dentro de uma string JS entre aspas duplas, o payload JSON injetado precisa ter os caracteres
" internos escapados com \":
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)
Saída resultante:
var _rs = ["[\"news\",\"tech\",\"release\"]", /* … */];
Em tempo de execução: JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"].
Se você esquecer o escape, o navegador verá aspas desbalanceadas e lançará um SyntaxError. O Padrão 2 evita isso
completamente ao usar crases.
Vários placeholders em um mesmo programa
Os três padrões podem ser combinados. Uma única regex de reservedStrings com alternância pode corresponder a todos os
formatos de placeholder que o seu motor de templates emite:
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
reservedNames: ['^__INITIAL_STATE__$'],
reservedStrings: ['\\{\\{[^}]+\\}\\}']
});
Você pode misturar placeholders de identificador (para valores JS brutos) e placeholders de string (para JSON renderizado) no mesmo código-fonte - escolha caso a caso, com base no que o servidor realmente vai injetar.
Notas de compatibilidade
- Não reutilize placeholders entre identificadores e strings. Um nome reservado como
__TOKEN__e uma string reservada que corresponda a__TOKEN__descrevem dois caminhos de código diferentes (array de expressões reservadas vs. array de strings reservadas). Use formatos textuais distintos para cada caso - por exemplo, uma convençãoUPPER_SNAKEpara placeholders de identificador e um formato envolvido por delimitadores ({{ ... }},%{...},<<<...>>>) para placeholders de string. Assim, um bug em uma das regex não pode corresponder silenciosamente à outra. - As regex de
reservedStringssão aplicadas aos valores brutos das strings. A regex é comparada com o valor da string em tempo de execução, não com o texto do código-fonte.\{\{[^}]+\}\}corresponde a strings que contêm{{.something}}(ou qualquer outro formato{{...}}). Se o seu placeholder puder estar envolvido por conteúdo extra ("prefix-{{.Field}}-suffix"), a regex ainda corresponde, mas a string inteira é preservada - planeje a sua substituição levando isso em conta. - Funciona com qualquer motor de templates. Embora os exemplos usem a sintaxe do
text/templatedo Go, nada na integração com o javascript-obfuscator é específico de Go. Qualquer coisa capaz de fazer uma substituição em nível de texto na saída do obfuscator funciona: Rails ERB, Django, short-tags do PHP, sed em um pipeline de CI, etc. Escolha delimitadores que o seu motor emita naturalmente e que não colidam com a sintaxe JS real.
