All detection rules and script modules in db, db_extra, and db_custom are DiE-JS. Format them as JavaScript with the formatter built into Microsoft Visual Studio Code.
Do not use an extension-specific formatter as the primary formatter for .sg files. In VS Code, the expected formatter is shown as TypeScript and JavaScript Language Features and has the identifier vscode.typescript-language-features. The repository .prettierrc keeps auxiliary tooling broadly compatible, but it does not replace the built-in VS Code formatter for DiE-JS.
This document defines the style for new rules and for code touched by a change. Some old database files predate the standard and contain legacy formatting. Do not copy a legacy exception into new code, and do not reformat an unrelated large file merely to make it match this document.
The PE heuristic engine is a reference for spacing only. It is a specialized module maintained exclusively by its author: do not copy its internal data structures, detector blocks, or result-building code into ordinary rules, and do not modify it as part of unrelated formatting work.
Associate .sg files with JavaScript, select the built-in formatter, and run Format Document (Shift+Alt+F on Windows/Linux) before every commit.
The following VS Code settings reproduce the intended baseline:
{
"files.associations": {
"*.sg": "javascript"
},
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features",
"editor.detectIndentation": false,
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.tabSize": 4
},
"javascript.format.enable": true,
"javascript.format.insertSpaceAfterCommaDelimiter": true,
"javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
"javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": true,
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
"javascript.format.insertSpaceAfterSemicolonInForStatements": true,
"javascript.format.insertSpaceBeforeAndAfterBinaryOperators": true,
"javascript.format.insertSpaceBeforeFunctionParenthesis": false,
"javascript.format.placeOpenBraceOnNewLineForControlBlocks": false,
"javascript.format.placeOpenBraceOnNewLineForFunctions": false,
"javascript.format.semicolons": "insert"
}If Format Document With... offers several choices, choose Configure Default Formatter, then select TypeScript and JavaScript Language Features. Prettier, Beautify, ESLint fixes, and other extensions must not silently take ownership of .sg formatting.
The formatter handles indentation and horizontal spacing. It intentionally preserves much of the author's vertical spacing, so the blank-line rules below still need to be applied by hand.
Use this as the starting point for a normal detection rule:
// Detect It Easy: detection rule file
// Author: Your Name <you@example.com>
// Optional reference URL
meta("compiler", "Example Compiler");
function detect() {
if (PE.isSectionNamePresent(".lz-algo")) {
sVersion = "1.0";
sOptions = "LZMA";
bDetected = true;
}
sLang = "C/C++";
return result();
}Remove fields that the rule does not need. Do not leave empty assignments merely to resemble the template.
A standalone rule is laid out in this order:
// Detect It Easy: detection rule fileon the first line.- One empty line. (optional, but not recommended)
- Author or co-author comments, kept together without empty lines between them.
- One empty line.
- Optional references or short provenance comments.
meta("type", "name");immediately after the reference block.- One empty line.
- Optional
includeScript("module");calls, one per line and kept together. - One empty line.
function detect() { ... }.- Helper functions, if required.
If there is no author block, reference block, meta(), or include block, omit that block and its associated separator. Never create several empty sections just because an optional part is absent.
Example with a shared module:
// Detect It Easy: detection rule file
// Author: Your Name <you@example.com>
// Reference URL
meta("compiler", "Example Compiler");
includeScript("ExampleSharedModule");
function detect() {
if (bExampleCompiler) {
bDetected = true;
}
return result();
}The engine provides the following result variables. Do not declare local variables with the same names.
| Field | Rule |
|---|---|
bDetected |
Set to true only after the detection conditions have succeeded. A normal rule does not produce a result without it. |
sName |
Optional runtime override for the name supplied by meta(). Use it when one file contains several detectable variants. |
sVersion |
Optional version or build text. Keep it short and do not repeat the product name. |
sOptions |
Optional qualifiers such as architecture, mode, compiler target, modification state, or embedded component. |
sLang |
Optional language result. This field is rare and belongs mainly in compiler rules. Place it at the end of detect(), immediately before the empty line that precedes return result();. Libraries, packers, and other rules may set it only when the evidence is strong and unambiguous. |
sType |
Very rare category override for a multi-purpose rule. Prefer a correct meta() category in ordinary rules. |
In the current database, roughly four out of five sLang assignments belong to compiler rules. Do not set sLang simply because a tool is commonly associated with a language.
Assign ordinary descriptive fields before bDetected:
if (matched) {
sVersion = "2.0";
sOptions = "modified";
bDetected = true;
}sLang is the deliberate exception. Assign it once, after the detection logic and immediately before the final blank line and return result();:
sLang = "Rust";
return result();
}End a normal detect() function exactly like this:
}
return result();
}There is one empty line before return result();. There is no empty line between return result(); and the closing brace. This spacing is required even when the detection body is short.
- Use 4 spaces for every indentation level.
- Never use tabs. The database formatting workflow converts tabs to four spaces, but contributors must not rely on CI to repair them.
- A nested block adds exactly four spaces.
- Continuation lines add one additional indentation level unless a deeper syntactic level requires more.
caseanddefaultlabels are indented once insideswitch; their statements are indented once more.
switch (value) {
case 1:
sVersion = "1.0";
break;
default:
break;
}Do not align unrelated assignments with large runs of spaces. Indentation describes structure; it is not a table-layout tool.
The following forms are required:
| Construct | Required form |
|---|---|
| Control-flow keyword | if (condition), for (...), while (...), switch (value) |
| Named function | function detect() |
| Anonymous function | function () { ... } |
| Function call | result(), not result () |
| Array access | items[index], not items [index] or items[ index ] |
| Unary operators | !detected, i++, --count |
| Binary operators | left === right, offset + size, flags & mask |
| Assignment | sVersion = "1.0"; |
| Ternary | condition ? yes : no |
| Comma | call(first, second) |
| Object property | { name: ".text", size: 512 } |
| Line comment | // Comment text |
| Inline comment | statement; // Explanation |
Rules derived from this table:
- Put one space after
if,for,while,switch, andcatchbefore(. - Do not put a space between a function name and
(. - Put one space on both sides of assignment, comparison, arithmetic, bitwise, and logical operators.
- Do not put spaces just inside
()or[]. - Put one space after each comma and none before it.
- Put one space after the colon in an object property and none before it.
- Opening braces stay on the same line as the function or control statement.
- Write
} else {,} else if (...) {,} catch (...) {, and} finally {on one line. - Use double quotes for strings. Single quotes inside a DiE byte signature are signature syntax and remain inside the surrounding double-quoted JavaScript string.
- Terminate statements with semicolons.
- Prefer strict equality (
===and!==) in new code unless type coercion is an intentional, documented part of the check. - Never leave trailing spaces at the end of a line.
Use braces for every multi-line control block:
if (matched) {
sVersion = "1.0";
bDetected = true;
} else if (fallbackMatched) {
bDetected = true;
}A single short guard or assignment may remain on one line when it is genuinely easier to read:
if (!section) return false;
if (matched) bDetected = true;Do not place two unrelated statements on one line. Do not compress a multi-branch if, loop, or switch merely to reduce line count.
VS Code does not need to force every signature string below a fixed column. Long byte signatures, regular expressions, URLs, and API calls may stay on one line when splitting them would obscure the pattern. Wrap logical structure, not arbitrary character counts.
For a multi-line condition, keep the first condition after if (. Continue the following conditions one indentation level deeper than the if statement, and keep ) { on the final condition line:
if (PE.getNumberOfSections() > 3 &&
PE.getEntryPointSection() === PE.nLastSection &&
PE.getImportSection() === 2) {
bDetected = true;
}Keep nested grouping explicit without moving the outer opening parenthesis onto a separate line:
if ((!PE.is64() && PE.getNumberOfSections() === 3) ||
(PE.is64() && PE.getNumberOfSections() === 4)) {
bDetected = true;
}For a long declaration, place the comma at the end of the current declaration and align following names one indentation level deeper:
var sectionOffset = PE.getSectionFileOffset(index),
sectionSize = PE.getSectionFileSize(index),
sectionName = PE.getSectionName(index);Split a structured value across lines when each field carries separate information:
var sectionInfo = {
name: PE.getSectionName(index),
fileOffset: PE.getSectionFileOffset(index),
fileSize: PE.getSectionFileSize(index)
};Do not add a comma after the last array item, object property, parameter, or argument solely for formatting. Preserve a trailing comma only when the surrounding module already uses it intentionally.
Blank lines are part of the project style; the built-in formatter will not create all of them for you.
For an ordinary standalone rule:
- Use one empty line between the file header, attribution, metadata/include block, and
detect(). - Use one empty line between independent logical steps inside
detect(). - Keep tightly related assignments together with no empty lines between them.
- Use one empty line before a nested check when it starts a distinct decision.
- Use exactly one empty line before the final
return result();. - Do not start a short function with an empty line immediately after
{. - Do not put an empty line between
return result();and}.
For a large script module with clearly separated independent passes:
- Use three empty lines between top-level functions, JSDoc blocks, or
#regionsections. - Use four empty lines between major independent detector blocks inside a long scan function.
- Use one empty line inside a detector block to separate declaration, condition, result construction, and logging.
- Keep the wider three/four-line rhythm out of small standalone signature rules.
The last visible line of a file is the closing }. Do not add an empty line after it. A terminal line-ending character may be preserved by the editor or Git; it is not the same as an extra blank line. Do not create mixed LF/CRLF endings or make line-ending-only changes.
Short arrays may stay on one line:
var names = [".text", ".rdata", ".data"];Use one item per line when entries contain tuples, comments, regular expressions, or long signatures:
var signatures = [
["1.0", [".text", ".rdata"]],
["2.0", [/^\.textbss$/, ".data"]]
];An entry schema must be represented by the correct nesting. If element [1] is a list of expected section names, keep all of those names inside that list:
// Correct
["1.0", [".text", ".rdata"]];
// Wrong: ".rdata" is no longer part of element [1]
["1.0", ".text", ".rdata"];Use a multi-line object when it represents an ordinary structured value with several meaningful fields:
var scanRange = {
offset: PE.getEntryPointOffset(),
size: 0x200
};- Use
constwhen the binding is not reassigned. - Use
varfor mutable locals and for compatibility with the DiE-JS runtime used by existing modules. - Avoid
letin new database rules unless the target runtime and the surrounding module already require and support it. - Use descriptive
camelCasenames for locals and functions. - Boolean names should normally begin with
is,has,can, orshould. - Constants shared across a module may use an established module-specific convention; do not rename engine API constants for style alone.
- Do not shadow engine result fields such as
sName,sVersion,sOptions,sLang,sType, orbDetected.
- Start a normal line comment with
//: two slashes followed by one space. - Place a source URL directly above the rule or check it supports.
- Explain why a condition exists, not what an obvious assignment does.
- Use inline comments sparingly and separate them from code with one space.
- Preserve comments embedded in signature strings; they are data, not JavaScript formatting.
- Use JSDoc for reusable helpers whose parameters, return value, or side effects are not immediately obvious.
- Keep
@param,@returns, and@throwsdescriptions grammatical and synchronized with the code.
/**
* Checks whether all expected markers are present.
*
* @param {Array} markers - Marker names to check.
* @returns {boolean} True when every marker is present.
*/
function areMarkersPresent(markers) {
for (var i = 0; i < markers.length; i++) {
if (!PE.isSectionNamePresent(markers[i])) return false;
}
return true;
}Formatting cannot rescue a weak rule. New detection code should also follow these rules:
- Cheap structural checks and relevant antipatterns are mandatory before expensive byte searches.
- Never use
findSignature(),isSignaturePresent(),isSignatureInSectionPresent(), or a similar search helper as the first line of defence. Conditions run from left to right, so put cheap gates first. - Bound every content search to the smallest meaningful section, resource, overlay, or header region.
- Cache values that are expensive or used repeatedly.
- Combine weak indicators instead of presenting one common string as a definitive family match.
- Add exclusions or antipatterns whenever a legitimate producer can share a marker.
- Sanitize names read from malformed files before placing them in output.
- Do not edit generated files in
dbs_minby hand. Regenerate them with the repository tooling.
Rules that put an expensive signature scan first, omit applicable antipatterns, or perform an avoidable whole-file search are rejected during review. See the mandatory detection-rule performance requirements.
Minified modules, generated database output, vendored code, and large legacy rules may also differ. Touch only the lines needed for the change unless a maintainer explicitly requests a full cleanup.
- Open every changed
.sgfile as JavaScript in VS Code. - Run Format Document with TypeScript and JavaScript Language Features.
- Confirm indentation is four spaces and contains no tabs.
- Remove trailing whitespace.
- Restore the required blank line before every final
return result();. - Check that
meta(), includes,detect(), and helpers are in the expected order. - Confirm ordinary optional result fields are assigned before
bDetected = true. - If the rule sets
sLang, place it after the detection logic and immediately before the blank line precedingreturn result();. - Compile or minify the changed scripts to catch syntax errors.
- Review the diff for unrelated formatting churn and mixed line endings.