If a string literal or evaluated snippet still expects the original identifier, renaming can create a runtime mismatch and throw an error. The safer response is to exclude the affected name from renaming or adjust the transformation order so earlier passes do not remove the link between the declaration and the reference. That preserves execution while still improving obfuscation.
Why local renaming fails when names are embedded in strings
Local renaming is safe for ordinary symbol references, but string-based references are not ordinary references. If code stores an identifier inside a string literal, generated snippet, template, or evaluated expression, that text may still depend on the original name. Renaming only the declaration and direct uses can break the hidden dependency and change runtime behaviour.
The core issue is that the transformation is no longer purely lexical. A compiler, minifier, or obfuscator may see the declaration as unused by text, but the runtime still resolves the old name from the string. That is why a rename that looks correct in the abstract can still produce a mismatch once the program executes.
In practice, the safe outcome depends on whether the string is data or a name-bearing reference. If the string is just user-visible text, renaming does not matter. If the string is later interpreted as code or used for reflective lookup, the transformation must preserve the identifier or rewrite every dependent string at the same time.
What breaks at runtime after a rename
When the original identifier disappears but a string still expects it, the lookup or evaluation step fails. The result is typically an undefined reference, a missing binding, a failed property access, or an exception raised by the interpreter or runtime loader. The failure may not appear until the relevant path is executed, which makes it easy to miss during static review.
This is especially important when the codebase uses reflection, dynamic dispatch, code generation, or configuration-driven lookup. Those patterns turn names into interface points, so the name itself becomes part of the contract. A renamer that changes the contract without understanding it can preserve syntax while breaking semantics.
That is why a transformation pipeline should treat string-based name references as a dependency, not as incidental text. The link between declaration and reference may be invisible to the parser, but it is still operationally real.
How to keep renaming safe without losing obfuscation value
The most reliable approach is to exclude any symbol that is consumed by a string-based reference, or to run the transformation in an order that resolves those references before renaming removes the original link. Where possible, rename only after the code has been rewritten so the declaration and every dependent reference move together.
If the environment includes dynamic evaluation, generated source, or reflective access, the renamer needs a conservative mode. That usually means preserving externally visible names, maintaining a name map for dependent text, or skipping only the symbols that cannot be proven safe to rename. A partial rename is better than a broken program.
When you are deciding between safety and obfuscation strength, prefer preserving execution over maximizing renaming coverage. Obfuscation loses value if the code no longer runs, and a narrow exception for name-bearing strings is usually cheaper than debugging a subtle runtime fault later.
Risk and Threat Considerations
String-based references create a fragile dependency between source text and executable behaviour. The main risk is silent breakage: the code can pass transformation and still fail only when the affected path is executed, which makes the defect harder to detect in review and testing.
Failure mechanism: A renamer updates the declaration and direct references, but an evaluated string, reflection lookup, or generated snippet still expects the original name. The runtime then resolves the wrong identifier or no identifier at all and throws an error.
Impact: Obfuscation can become a correctness defect, causing feature failure, partial outages, or hard-to-trace runtime exceptions in code paths that rely on names as interface points.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
OWASP ASVS, OWASP SAMM and NIST SP 800-53 Rev 5 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| OWASP ASVS | V15 — Secure Coding and Architecture | Renaming safety depends on preserving runtime semantics across code transformations. |
| Recommendation — Validate code transformations against runtime behaviour before releasing obfuscated builds. | ||
| OWASP SAMM | Software Assurance Maturity Model | The issue is a build-time software engineering control that affects secure delivery quality. |
| Recommendation — Embed transformation checks into the build process and gate releases on semantic test coverage. | ||
| NIST SP 800-53 Rev 5 | CM-3 — Configuration Change Control | Renaming is a controlled code change that can break dependencies if not managed carefully. |
| Recommendation — Review and approve transformation rules that can alter executable references. | ||
Practitioner Guidance
What to verify: Treat any string that names a variable, function, class, or exported symbol as a dependency of the rename pass. Verify whether the string is ever passed into evaluation, reflection, template expansion, or lookup logic before allowing the symbol to be renamed.
Decision rule: If a name appears in code as both an identifier and a string, either keep the original name stable or update every dependent string in the same transformation stage. If you cannot prove complete coverage, do not rename it.
Practitioner takeaway: Safe renaming is not just about rewriting identifiers, it is about preserving every runtime path that still interprets the old name as meaningful.
Related resources from NHI Mgmt Group
- What breaks when security tools keep using string-based configuration and flat resource references in Kubernetes?
- What happens when a format string bug is discovered inside native code used by higher-level languages?
- How should security teams use cross-references to trace where a string or data item is used in compiled code?
- What happens when secure code review is applied uniformly instead of by risk?