Skip to content

Commit

Permalink
fore: 可用版本v0.1.0固定与提交
Browse files Browse the repository at this point in the history
  • Loading branch information
zhenxianluo committed Mar 10, 2024
0 parents commit 0fffebe
Show file tree
Hide file tree
Showing 35 changed files with 753 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.DS_Store
node_modules/
output/
npm-debug.log
yarn-error.log
.idea
.vscode
package-lock.json
yarn.lock
.history
*.swp
build/
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
!bin/
!browser/
!utils/
30 changes: 30 additions & 0 deletions .release-it.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module.exports = {
github: {
release: true
},
git: {
commitMessage: "release: v${version}"
},
npm: {
publish: false
},
hooks: {
"after:bump": "echo 更新版本成功"
},
plugins: {
'@release-it/conventional-changelog': {
preset: 'conventionalcommits',
infile: 'CHANGELOG.md',
sameFile: true,
releaseRules: [
{ type: 'feat', release: 'minor' },
{ type: 'fix', release: 'patch' },
{ type: 'docs', release: 'patch' },
{ type: 'style', release: 'patch' },
{ type: 'refactor', release: 'patch' },
{ type: 'perf', release: 'patch' },
{ type: 'test', release: 'patch' },
],
},
},
};
120 changes: 120 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<h1 align="center">
<img width="100" height="100" src="logo.svg" alt=""><br>
sdenv
</h1>

sdenv是一个javascript运行时补环境框架,与github上其它补环境框架存在较大区别,sdenv是站在巨人的肩膀上实现的,依赖于jsdom的强大dom仿真能力,sdenv可以真实模拟浏览器执行环境,作者在固定随机数与添加[sdenv-extend](https://github.com/pysunday/sdenv-extend)的部分插件后可以达到**瑞数vmp代码在sdenv运行生成的cookie值与浏览器生成的cookie值一致**

* sdenv专用jsdom版本:[sdenv-jsdom](https://github.com/pysunday/sdenv-jsdom)
* sdenv多端环境提取:[sdenv-extend](https://github.com/pysunday/sdenv-extend)

## 依赖

作者开发时使用的是`v20.10.0`版本node,预期最低要求是18版本,由于未做其它版本可用性测试,因此建议使用sdenv的node版本大于等于`v20.10.0`

## 安装

由于`document.all`需要由c代码动态生成,而固定编译环境下的编译产物只能在相同编译环境下运行,因此安装sdenv后需要动态编译生成node文件

1. 安装:`npm i sdenv`
2. 编译c代码:`cd node_modules/sdenv && yarn build`

**在编译过程未实现自动化之前可直接clone项目使用**

## 使用

因为项目核心功能基于jsdom,且jsdom对dom的实现非常完善,因此使用sdenv之前建议有一定html与javascript语言开发基础,然后参考example目录下的样例文件:

1. [use-local](https://github.com/pysunday/sdenv/example/use-local/README.md)
```javascript
const fs = require('fs');
const path = require('path');
const { Script } = require("vm");
const logger = require('../../utils/logger');
const browser = require('../../browser/');
const { jsdomFromText } = require('../../utils/jsdom');

const baseUrl = "https://wcjs.sbj.cnipa.gov.cn"

const files = {
html: path.resolve(__dirname, 'output/makecode_input_html.html'),
js: path.resolve(__dirname, 'output/makecode_input_js.js'),
ts: path.resolve(__dirname, 'output/makecode_input_ts.json'),
}

function getFile(name) {
const filepath = files[name];
if (!filepath) throw new Error(`getFile: ${name}错误`);
if (!fs.existsSync(filepath)) throw new Error(`文件${filepath}不存在,请使用rs-reverse工具先获取文件`);
return fs.readFileSync(filepath);
}

function initBrowser(window, cookieJar) {
window.$_ts = JSON.parse(getFile('ts'));
window.onbeforeunload = async (url) => {
const cookies = cookieJar.getCookieStringSync(baseUrl);
logger.debug('生成cookie:', cookies);
process.exit();
}
browser(window, 'chrome');
}

async function loadPages() {
const htmltext = getFile('html');
const jstext = getFile('js');
const [jsdomer, cookieJar] = jsdomFromText({
url: `${baseUrl}/sgtmi`,
referrer: `${baseUrl}/sgtmi`,
contentType: "text/html",
runScripts: "outside-only",
})
const dom = jsdomer(htmltext);
initBrowser(dom.window, cookieJar);
new Script(jstext).runInContext(dom.getInternalVMContext());
}

loadPages()
```
2. [use-remote](https://github.com/pysunday/sdenv/example/use-remote/README.md)
```javascript
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
const logger = require('../../utils/logger');
const browser = require('../../browser/');
const { jsdomFromUrl } = require('../../utils/jsdom');
const baseUrl = "https://wcjs.sbj.cnipa.gov.cn"
async function loadPages() {
const [jsdomer, cookieJar] = jsdomFromUrl({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
});
const dom = await jsdomer(`${baseUrl}/sgtmi`);
window = dom.window
window.onbeforeunload = async (url) => {
const cookies = cookieJar.getCookieStringSync(baseUrl);
logger.debug('生成cookie:', cookies);
process.exit();
}
browser(window, 'chrome');
}
loadPages()
```

## sdenv-extend使用说明

为了模拟浏览器执行环境,需要将node环境与浏览器环境共有代码进行提取,并提供返回环境对象用于sdenv内window与dom内容补充使用。

sdenv-extend初始化只执行一次,初始化成功后生成的环境对象可以使用`Object.sdenv()`(vm中使用非node)获取。

sdenv-extend具体功能可参考项目内README文档。

## 声明

该项目的开发基于瑞数vmp网站,不能保证在其它反爬虫产品稳定使用,出现问题请及时提issues或者提pull参与共建!

由于初期版本只做了chrome浏览器的拟真,且项目文档不完善,作者会陆续补充,可以加入技术交流群与订阅号持续关注!

添加作者微信进技术交流群:howduudu_tech(备注sdenv)

订阅号会定期发表技术文章:码功
51 changes: 51 additions & 0 deletions bin/documentAll.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <node.h>
namespace documentAll {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Value;
using v8::Null;
using v8::Array;

void MyFunctionCallback(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(Null(isolate));
}

void GetDocumentAll(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<ObjectTemplate> obj_template = ObjectTemplate::New(isolate);
obj_template->MarkAsUndetectable();
obj_template->SetCallAsFunctionHandler(MyFunctionCallback);
Local<Object> obj = obj_template->NewInstance(context).ToLocalChecked();
if (args.Length() > 0 && args[0]->IsObject()) {
Local<Object> argObj = args[0]->ToObject(context).ToLocalChecked();
Local<Array> propertyNames = argObj->GetPropertyNames(context).ToLocalChecked();
for (uint32_t i = 0; i < propertyNames->Length(); ++i) {
Local<Value> key = propertyNames->Get(context, i).ToLocalChecked();
Local<Value> value = argObj->Get(context, key).ToLocalChecked();
(void)obj->Set(context, key, value);
}
}
args.GetReturnValue().Set(obj);
}

void Init(Local<Object> exports, Local<Object> module) {
Isolate* isolate = exports->GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<FunctionTemplate> method_template = FunctionTemplate::New(isolate, GetDocumentAll);
exports->Set(context, String::NewFromUtf8(isolate, "getDocumentAll").ToLocalChecked(), method_template->GetFunction(context).ToLocalChecked()).FromJust();
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
}

Binary file added bin/documentAll.node
Binary file not shown.
11 changes: 11 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"targets": [
{
"target_name": "documentAll",
"sources": [
"bin/documentAll.cc",
]
}
]
}

19 changes: 19 additions & 0 deletions browser/chrome/RTCPeerConnection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const sdenv = require('sdenv-extend').sdenv();
const window = sdenv.memory.sdWindow;

function RTCPeerConnection() {
if (!(this instanceof RTCPeerConnection)) {
throw new TypeError("Uncaught TypeError: Failed to construct 'RTCPeerConnection': Please use the 'new' operator, this DOM object constructor cannot be called as a function.");
}
this.createDataChannel = function(...params) {
// window.console.log(`【RTCPeerConnection RTCPeerConnection】调用,参数:${params}`)
}
this.createOffer = function(...params) {
// window.console.log(`【RTCPeerConnection createOffer】调用,参数:${params}`)
}
}
sdenv.tools.setNativeFuncName(RTCPeerConnection, 'RTCPeerConnection');
sdenv.tools.setNativeObjName(RTCPeerConnection.prototype, 'RTCPeerConnection');


window.RTCPeerConnection = RTCPeerConnection;
39 changes: 39 additions & 0 deletions browser/chrome/chrome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const sdenv = require('sdenv-extend').sdenv();

sdenv.memory.sdWindow.chrome = {
app: {
isInstalled: false,
InstallState: {
DISABLED: "disabled",
INSTALLED: "installed",
NOT_INSTALLED: "not_installed",
},
RunningState: {
CANNOT_RUN: "cannot_run",
READY_TO_RUN: "ready_to_run",
RUNNING: "running",
},
getDetails: function () {},
getIsInstalled: function() {},
installState: function() {},
runningState: function() {},
},
csi: function() {},
loadTimes: function() {
return {
"requestTime": 1700779741.985,
"startLoadTime": 1700779741.985,
"commitLoadTime": 1700779742.021,
"finishDocumentLoadTime": 0,
"finishLoadTime": 0,
"firstPaintTime": 0,
"firstPaintAfterLoadTime": 0,
"navigationType": "Reload",
"wasFetchedViaSpdy": false,
"wasNpnNegotiated": true,
"npnNegotiatedProtocol": "http/1.1",
"wasAlternateProtocolAvailable": false,
"connectionInfo": "http/1.1"
}
}
}
12 changes: 12 additions & 0 deletions browser/chrome/ctorRegistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const logger = require('@utils/logger');
const utils = require('sdenv-jsdom/lib/jsdom/living/generated/utils.js');
const sdenv = require('sdenv-extend').sdenv();
const window = sdenv.memory.sdWindow;

const ctorRegistry = window[utils.ctorRegistrySymbol]
window[utils.ctorRegistrySymbol] = new window.Proxy(ctorRegistry, {
get(target, propKey, receiver) {
logger.trace('proxy ctorRegistry get', propKey);
return window.Reflect.get(target, propKey, receiver);
}
})
5 changes: 5 additions & 0 deletions browser/chrome/document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const getDocumentAll = require('@bin/documentAll').getDocumentAll;
const sdenv = require('sdenv-extend').sdenv();
const window = sdenv.memory.sdWindow;

window.document.all = getDocumentAll({ length: 3 });
12 changes: 12 additions & 0 deletions browser/chrome/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require('./window');
require('./document');
require('./navigation');
require('./navigator');
require('./chrome');
require('./visualViewport');
require('./styleMedia');
// require('./webkitRequestFileSystem');
require('./ctorRegistry');
require('./location');
require('./indexedDB');
require('./RTCPeerConnection');
16 changes: 16 additions & 0 deletions browser/chrome/indexedDB.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const sdenv = require('sdenv-extend').sdenv();
const window = sdenv.memory.sdWindow;

const IDBFactory = function IDBFactory() {
throw new TypeError("Illegal constructor");
}

const indexedDB = {
__proto__: IDBFactory.prototype
};

sdenv.tools.setNativeFuncName(IDBFactory, 'IDBFactory');
sdenv.tools.setNativeObjName(indexedDB, 'IDBFactory');

window.IDBFactory = IDBFactory;
window.indexedDB = indexedDB;
10 changes: 10 additions & 0 deletions browser/chrome/location.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const sdenv = require('sdenv-extend').sdenv();
const window = sdenv.memory.sdWindow;

Object.defineProperty(window.location, 'replace', {
...Object.getOwnPropertyDescriptor(window.location, 'replace'),
writable: false,
value: function(url) {
sdenv.tools.exit({ url });
}
});
20 changes: 20 additions & 0 deletions browser/chrome/navigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const sdenv = require('sdenv-extend').sdenv();
const window = sdenv.memory.sdWindow;

[window.Navigation, window.navigation] = sdenv.tools.getNativeProto('Navigation', 'navigation', {
canGoBack: false,
canGoForward: false,
oncurrententrychange: null,
onnavigate: null,
onnavigateerror: null,
onnavigatesuccess: null,
transition: null,
currentEntry: {
id: 'c72e7c89-2c22-47b6-86b8-e83db973ad22',
index: 1,
key: 'd6cc1590-0028-48e9-b6e7-b489d28d8481',
ondispose: null,
sameDocument: true,
url: 'http://example.com',
}
});
Loading

0 comments on commit 0fffebe

Please sign in to comment.