Skip to content

Commit ec747e0

Browse files
author
David Sheldrick
committed
hook up obfuscator
1 parent 67a6e2f commit ec747e0

8 files changed

+272
-17
lines changed

package.json

+19-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
"author": "David Sheldrick",
88
"license": "MIT",
99
"dependencies": {
10+
"@types/app-root-path": "^1.2.4",
1011
"@types/babel-core": "^6.25.3",
12+
"@types/babel-generator": "^6.25.1",
1113
"@types/node": "^9.3.0",
1214
"@types/semver": "^5.4.0",
1315
"app-root-path": "^2.0.1",
16+
"babel-generator": "^6.26.0",
1417
"javascript-obfuscator": "^0.13.0",
1518
"jju": "^1.3.0",
1619
"semver": "^5.4.1",
@@ -25,7 +28,9 @@
2528
},
2629
"typings": "dist/index.d.ts",
2730
"lint-staged": {
28-
"*.ts": ["prettier --no-semi --single-quote --trailing-comma es5"]
31+
"*.ts": [
32+
"prettier --no-semi --single-quote --trailing-comma es5"
33+
]
2934
},
3035
"devDependencies": {
3136
"@types/jest": "^22.0.1",
@@ -39,13 +44,25 @@
3944
"prettier": "^1.10.2",
4045
"react-native": "^0.45.1",
4146
"ts-jest": "^22.0.1",
47+
"ts-node": "^4.1.0",
4248
"typescript": "^2.6.2"
4349
},
4450
"jest": {
4551
"transform": {
4652
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
4753
},
54+
"transformIgnorePatterns": [
55+
"node_modules/(?!(javascript-obfuscator)/)"
56+
],
57+
"moduleNameMapper": {
58+
"javascript-obfuscator": "<rootDir>/node_modules/javascript-obfuscator/index.ts"
59+
},
4860
"testRegex": "src/.*(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
49-
"moduleFileExtensions": ["ts", "tsx", "js", "json"]
61+
"moduleFileExtensions": [
62+
"ts",
63+
"tsx",
64+
"js",
65+
"json"
66+
]
5067
}
5168
}

src/__tests__/files/es5.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var y = "yes"
2+
module.exports = function banana() {
3+
return y
4+
}

src/__tests__/obfuscateCode.test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as fs from "fs"
2+
3+
jest.mock("javascript-obfuscator", () => ({
4+
obfuscate() {
5+
return {
6+
getObfuscatedCode() {
7+
return "this code is obfuscated"
8+
},
9+
getSourceMap() {
10+
return ""
11+
},
12+
}
13+
},
14+
}))
15+
16+
import { obfuscateCode } from "../obfuscateCode"
17+
18+
describe("obfuscateCode", () => {
19+
it("obfuscates code", () => {
20+
const filename = require.resolve("./files/es5.js")
21+
const es5code = fs.readFileSync(filename).toString()
22+
expect(obfuscateCode(es5code, {})).toBe("this code is obfuscated")
23+
})
24+
})

src/getMetroTransformer.ts

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Node } from "babel-core"
2-
import { RawSourceMap } from "source-map"
2+
import { RawSourceMap, SourceMapConsumer } from "source-map"
33
import * as semver from "semver"
44
import { MetroRawSourceMap } from "./composeSourceMaps"
5+
import * as babylon from "babylon"
6+
import traverse from "babel-traverse"
57

68
export interface MetroTransformerResult {
79
ast?: Node
@@ -47,3 +49,40 @@ export function getMetroTransformer(
4749
)
4850
}
4951
}
52+
53+
export interface ReactNativeObfuscatingTransformerDefaultResult {
54+
code: string
55+
map: string
56+
}
57+
58+
export function maybeTransformMetroResult(
59+
{ code, map }: ReactNativeObfuscatingTransformerDefaultResult,
60+
reactNativeMinorVersion: number = getReactNativeMinorVersion(),
61+
): ReactNativeObfuscatingTransformerDefaultResult | { ast: Node } {
62+
if (reactNativeMinorVersion >= 52) {
63+
// convert code and map to ast
64+
const ast = babylon.parse(code, {
65+
sourceType: "module",
66+
})
67+
68+
const mapConsumer = new SourceMapConsumer(map as any) // upstream types are wrong
69+
;(traverse as any).cheap(ast, (node: Node) => {
70+
if (node.loc) {
71+
const originalStart = mapConsumer.originalPositionFor(node.loc.start)
72+
if (originalStart.line) {
73+
node.loc.start.line = originalStart.line
74+
node.loc.start.column = originalStart.column
75+
}
76+
const originalEnd = mapConsumer.originalPositionFor(node.loc.end)
77+
if (originalEnd.line) {
78+
node.loc.start.line = originalEnd.line
79+
node.loc.start.column = originalEnd.column
80+
}
81+
}
82+
})
83+
84+
return { ast }
85+
} else {
86+
return { code, map }
87+
}
88+
}

src/obfuscateCode.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as Obfuscator from "javascript-obfuscator"
2+
import {
3+
convertMetroRawSourceMapToStandardSourceMap,
4+
composeSourceMaps,
5+
MetroRawSourceMap,
6+
} from "./composeSourceMaps"
7+
import { RawSourceMap } from "source-map/source-map"
8+
9+
export function obfuscateCode(
10+
code: string,
11+
options: Obfuscator.Options,
12+
): string {
13+
return Obfuscator.obfuscate(code, options).getObfuscatedCode()
14+
}
15+
16+
export function obfuscateCodePreservingSourceMap(
17+
code: string,
18+
map: string | RawSourceMap | MetroRawSourceMap,
19+
originlFilename: string,
20+
originalSource: string,
21+
options: Obfuscator.Options,
22+
): { code: string; map: string } {
23+
const obfuscationResult = Obfuscator.obfuscate(code, options)
24+
const obfuscationResultMap = obfuscationResult.getSourceMap()
25+
26+
if (!obfuscationResultMap) {
27+
throw new Error(
28+
"javascript-obfuscator did not return a source map for file " +
29+
originlFilename,
30+
)
31+
}
32+
33+
if (Array.isArray(map)) {
34+
map = convertMetroRawSourceMapToStandardSourceMap(
35+
map,
36+
originlFilename,
37+
originalSource,
38+
)
39+
}
40+
41+
return {
42+
code: obfuscationResult.getObfuscatedCode(),
43+
map: composeSourceMaps(
44+
map,
45+
obfuscationResultMap,
46+
originlFilename,
47+
originalSource,
48+
),
49+
}
50+
}

src/obfuscatingTransformer.ts

+67-8
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,98 @@
11
import * as crypto from "crypto"
22
import * as fs from "fs"
3+
import * as path from "path"
34
import * as JavaScriptObfuscator from "javascript-obfuscator"
5+
import { path as appRootPath } from "app-root-path"
6+
7+
import generate from "babel-generator"
48

59
import { getCallerFile } from "./getCallerFile"
6-
import { MetroTransformer, getMetroTransformer } from "./getMetroTransformer"
10+
import {
11+
MetroTransformer,
12+
getMetroTransformer,
13+
MetroTransformerResult,
14+
maybeTransformMetroResult,
15+
} from "./getMetroTransformer"
16+
import {
17+
obfuscateCode,
18+
obfuscateCodePreservingSourceMap,
19+
} from "./obfuscateCode"
720

8-
function getOwnCacheKey(upstreamCacheKey: string, callerFilename: string) {
21+
function getOwnCacheKey(upstreamCacheKey: string, configFilename: string) {
922
var key = crypto.createHash("md5")
1023
key.update(upstreamCacheKey)
1124
key.update(fs.readFileSync(__filename))
12-
key.update(fs.readFileSync(callerFilename))
25+
key.update(fs.readFileSync(configFilename))
1326
return key.digest("hex")
1427
}
1528

1629
export interface ObfuscatingTransformerOptions {
17-
match?: RegExp
30+
filter?(filename: string, source: string): boolean
1831
upstreamTransformer?: MetroTransformer
1932
obfuscatorOptions?: JavaScriptObfuscator.Options
33+
trace?: boolean
2034
}
2135

36+
const sourceDir = path.join(appRootPath, "src")
37+
2238
export function obfuscatingTransformer({
23-
match = /.*.js/,
39+
filter = filename => filename.startsWith(sourceDir),
2440
upstreamTransformer = getMetroTransformer(),
25-
..._options
41+
obfuscatorOptions: _obfuscatorOptions,
42+
...otherOptions
2643
}: ObfuscatingTransformerOptions): MetroTransformer {
2744
const callerFilename = getCallerFile()
2845

46+
const obfuscatorOptions: JavaScriptObfuscator.Options = {
47+
..._obfuscatorOptions,
48+
sourceMap: true,
49+
sourceMapMode: "separate",
50+
stringArray: false,
51+
}
52+
2953
return {
3054
transform(props) {
3155
const result = upstreamTransformer.transform(props)
32-
if (props.filename.match(match)) {
33-
console.log("obfuscating", props.filename)
56+
const resultCanBeObfuscated = result.code || result.ast
57+
58+
if (resultCanBeObfuscated && filter(props.filename, props.src)) {
59+
if (otherOptions.trace) {
60+
console.log("Obfuscating", props.filename)
61+
}
62+
63+
const { code, map }: MetroTransformerResult = result.code
64+
? result
65+
: result.ast
66+
? (generate(result.ast, {
67+
filename: props.filename,
68+
retainLines: true,
69+
sourceMaps: true,
70+
sourceFileName: props.filename,
71+
}) as MetroTransformerResult)
72+
: { code: "", map: "" }
73+
74+
if (!code) {
75+
return result
76+
} else if (!map) {
77+
return {
78+
code: obfuscateCode(code, obfuscatorOptions),
79+
}
80+
}
81+
82+
return maybeTransformMetroResult(
83+
obfuscateCodePreservingSourceMap(
84+
code,
85+
map,
86+
props.filename,
87+
props.src,
88+
obfuscatorOptions,
89+
),
90+
)
3491
}
92+
3593
return result
3694
},
95+
3796
getCacheKey() {
3897
return getOwnCacheKey(
3998
upstreamTransformer.getCacheKey

tsconfig.json

-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"moduleResolution": "node",
1111
"noUnusedLocals": true,
1212
"noUnusedParameters": true,
13-
"inlineSourceMap": true,
1413
"declaration": true,
1514
"baseUrl": ".",
1615
"paths": {
@@ -19,7 +18,6 @@
1918
]
2019
},
2120
"experimentalDecorators": true,
22-
"inlineSources": true
2321
},
2422
"include": [
2523
"typings",

0 commit comments

Comments
 (0)