JavaScript沙箱

news2024/11/29 4:49:47

1、什么是沙箱

在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常用于执行未经测试或者不受信任的程序或代码,它会为待执行的程序创建一个独立的执行环境,内部程序的执行不会影响到外部程序的运行。

  • 动态执行代码方法
    • eval
    • new Function
// eval 
const person = eval("({name:'张三'})");
console.log(person.name);


// new Function
const a = 1;

function sandbox() {
    const a = 2;
    return new Function('return a;');         // 这里的 a 指向最上面全局作用域内的 1
}

const f = sandbox();
console.log(f());

2、沙箱的应用场景

  • 在线代码编辑器,如 CodeSanbox、JS Bin 等在线代码编辑器在执行脚本时都会将程序放置在一个沙箱中,防止程序访问/影响主页面

  • Vue 模板表达式的计算是运行在一个沙盒之中的,在模板字符串中的表达式只能获取部分全局对象
    在这里插入图片描述

  • 插件机制,许多应用程序提供了插件(Plugin)机制,开发者可以书写自己的插件程序实现某些自定义功能。但插件的开发和运行都要遵循宿主程序的限制,这是因为插件是运行在沙箱中

  • 部分微前端框架,如qiankun

总而言之,只要遇到不可信的第三方代码,我们就可以使用沙箱将代码进行隔离,从而保障外部程序的稳定运行。如果不做任何处理地执行不可信代码,在前端中最直观的副作用/危害就是污染、篡改全局 window 状态,影响主页面功能。

3、JavaScript中的沙箱实现

要实现一个沙箱,其实就是去制定一套程序执行机制,在这套机制的作用下沙箱内部程序的运行不会影响到外部程序的运行。

  • 基于作用域隔离
  • 原生浏览器对象模拟
  • 天然的优质沙箱iframe

3.1、基于作用域隔离

要实现这样的一个效果,最直接的想法是程序中访问的所有变量均来自可靠或者自主实现的上下文环境而不会从全局的执行环境中取值,那么要实现变量的访问均来自一个可靠的上下文环境,我们需要为待执行程序构造一个作用域。

3.1.1、function scope

我们知道在JavaScript中的作用域(scope)只有全局作用域(global scope)、函数作用域(function scope)以及ES6以后的块级作用域(block scope)。如果要将一段代码中的变量、函数等的定义隔离出来,受限于JavaScript对作用域的控制,只能将这段代码封装到一个Function中,通过function scope来达到作用域隔离的目的。

(function foo(){
    const a = 1;
    console.log(a);
 })();// 无法从外部访问变量 
 
 console.log(a) // 抛出错误:"Uncaught ReferenceError: a is not defined"


// 执行上下文对象
const ctx = 
    func: variable => {
        console.log(variable)
    },
    foo: 'foo'
}

// 最简陋的沙箱
function poorestSandbox(code, ctx) {
    eval(code) // 为执行程序构造了一个函数作用域
}

// 待执行程序
const code = `
    ctx.foo = 'bar'
    ctx.func(ctx.foo)
`
poorestSandbox(code, ctx) // bar

在函数作用域内的不能从外部访问,它拥有独立的词法作用域,而且也不会污染全局。但是在函数内部可以访问到外部的作用域,只能说是一种“君子沙箱”

3.1.2、with

with是JavaScript中的一个关键字,扩展一个语句的作用域链。with会在作用域链的顶端添加一个新的作用域,改作用域的变量会加入with传入的对象,因此相较于外部环境其内部代码在查找变量时会优先在该对象上进行查找。

// 执行上下文对象
const ctx = {
    a: 2
}

// 非常简陋的沙箱
function veryPoorSandbox(code) {
     code = 'with (sandbox) {' + code + '}'
     return new Function('sandbox', code);
}


// 待执行程序
const a = 1
const code = `console.log(a)`
veryPoorSandbox(code)(ctx) // 2

问题来了,在提供的上下文对象中没有找到某个变量时,代码仍会沿着作用域链向上查找,所以它只能算一个“半沙盒”吧。

我们希望沙箱中的代码只在手动提供的上下文对象中查找变量,如果上下文对象中不存在该变量则直接报错或返回 undefined。

3.2、原生浏览器对象模拟

3.2.1、基于Proxy的沙箱机制

为了解决上述抛出的问题,我们借助 ES2015 的 Proxy,Proxy 可以代理一个对象,从而拦截并定义对象的基本操作。


function sandbox(code) {
    code = 'with (sandbox) {' + code + '}'
    const fn = new Function('sandbox', code)
    return function (sandbox) {
        const sandboxProxy = new Proxy(sandbox, {
            get(target, key) {
                return target[key]
            }
        })
        return fn(sandboxProxy)
    }
}
const test = {
    a: 'a',
    func(value){
        console.log(value)
    }
}
const code = 'func(a);' // a 
sandbox(code)(test)



function sandbox(code) {
    code = 'with (sandbox) {' + code + '}'
    const fn = new Function('sandbox', code)
    return function (sandbox) {
        const sandboxProxy = new Proxy(sandbox, {
            get(target, key) {
                return target[key]
            }
        })
        return fn(sandboxProxy)
    }
}
const test = {
    a: 'a',
    func(value){
        console.log(value)
    }
}
const testB = 'b'
const code = 'func(a); func(testB)'
sandbox(code)(test)

Proxy中的 get 和 set 方法只能拦截已存在于代理对象中的属性,对于代理对象中不存在的属性这两个钩子无感知。因此我们还需要使用 Proxy.has() 来拦截 with 代码块中的任意变量的访问。

function sandbox(code) {
    code = 'with (sandbox) {' + code + '}'
    const fn = new Function('sandbox', code)
    return function (sandbox) {
        const sandboxProxy = new Proxy(sandbox, {
            has(target,key){return true},
            get(target, key) {
                return target[key]
            }
        })
        return fn(sandboxProxy)
    }
}
const test = {
    a: 'a',
    func(value){
        console.log(value)
    }
}
const testB = 'b'
const code = 'func(a); func(testB)'  // 'a' undefined
sandbox(code)(test)

这样,我们就可以通过白名单的形式来控制变量的访问了。在白名单内的变量可以正常走作用域链的访问方式,不在白名单内的变量会继续判断是否存在沙箱自行维护的上下对象中,存在则正常访问,不存在则直接报错。

function sandbox(code) {
    code = 'with (sandbox) {' + code + '}'
    const fn = new Function('sandbox', code)
    
    const access_white_list = ['testB']

    return function (sandbox) {
        const sandboxProxy = new Proxy(sandbox, {
             has: (target, prop) => { // has 可以拦截 with 代码块中任意属性的访问
               if (access_white_list.includes(prop)) { // 在可访问的白名单内,可继续向上查找
                 return target.hasOwnProperty(prop)
               }

               if (!target.hasOwnProperty(prop)) {
                 throw new Error(`Invalid expression - ${prop}! You can not do that!`)
               }

               return true
            },
            get(target, key) {
                return target[key]
            }
        })
        return fn(sandboxProxy)
    }
}
const test = {
    a: 'a',
    func(value){
        console.log(value)
    }
}

const testB = 'b';
const testC = 'c'

const code = 'func(a); func(testB); func(testC)'  // a b error
sandbox(code)(test)

除了使用白名单数组外,官方给了一个解决方案:Symbol.unscopables。Symbol对象的Symbol.unscopables 属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被 with 环境排除。

function sandbox(code) {
    code = 'with (sandbox) {' + code + '}'
    const fn = new Function('sandbox', code)
    
    const unscopables = { testB: true};

    return function (sandbox) {
        const sandboxProxy = new Proxy(sandbox, {
             has: (target, prop) => { 
               return true
            },
            get(target, key) {
              if (key === Symbol.unscopables) return unscopables;
              
              return target[key]
            }
        })
        return fn(sandboxProxy)
    }
}
const test = {
    a: 'a',
    func(value){
        console.log(value)
    }
}

const testB = 'b';
const testC = 'c'

const code = 'func(a); func(testB); ' 
sandbox(code)(test)

3.2.2、基于属性 diff 的沙箱机制

由于 Proxy 为 ES6 引入的 API,在不支持 ES6 的环境下,我们可以通过一类原始的方法来实现所要的沙箱,即利用普通对象针对 window 属性值构建快照,用于环境的存储与恢复,并在应用卸载时对 window 对象修改做 diff 用于子应用环境的更新保存,该方法会污染全局 window。

qiankun 中也有该降级方案,被称作 SnapshotSandbox。

3.3、天然的优质沙箱iframe

不论是 Proxy 还是 diff ,其沙箱机制的方案都是通过模拟和代理来实现一个环境隔离。由于是模拟,因此子程序使用所有全局对象的同时不可避免的会影响外部的全局状态。

而 iframe 标签可以创造一个独立的浏览器原生级别的运行环境,这个环境实现了与主环境的隔离。在 iframe 中运行的脚本程序访问到的全局对象均是当前 iframe 执行上下文提供的,不会影响其父页面的主体功能,因此使用 iframe 来实现一个沙箱是目前最方便、简单、安全的方法。

4、qiankun沙箱实现

  • SnapshotSandbox
  • LegacySandbox
  • ProxySandbox

4.1、SnapshotSandbox

快照沙箱实现来说比较简单,主要用于不支持 Proxy 的低版本浏览器,原理是基于 diff 来实现的,在子应用激活或者卸载的时分别去通过快照的形式记录或者还原状态来实现沙箱。SnapshotSandbox 会濡染全局 window。

function iter(obj, callbackFn) {
    for (const prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            callbackFn(prop);
        }
    }
}

/**
 * 基于 diff 方式实现的沙箱,用于不支持 Proxy 的低版本浏览器
 */
class SnapshotSandbox {
    constructor(name) {
        this.name = name;
        this.proxy = window;
        this.type = 'Snapshot';
        this.sandboxRunning = true;
        this.windowSnapshot = {};
        this.modifyPropsMap = {};
        this.active();
    }
    //激活
    active() {
        // 记录当前快照
        this.windowSnapshot = {};
        iter(window, (prop) => {
            this.windowSnapshot[prop] = window[prop];
        });

        // 恢复之前的变更
        Object.keys(this.modifyPropsMap).forEach((p) => {
            window[p] = this.modifyPropsMap[p];
        });

        this.sandboxRunning = true;
    }
    //还原
    inactive() {
        this.modifyPropsMap = {};

        iter(window, (prop) => {
            if (window[prop] !== this.windowSnapshot[prop]) {
                // 记录变更,恢复环境
                this.modifyPropsMap[prop] = window[prop];
              
                window[prop] = this.windowSnapshot[prop];
            }
        });
        this.sandboxRunning = false;
    }
}
let sandbox = new SnapshotSandbox();
//test
((window) => {
    window.name = '张三'
    window.age = 18
    console.log(window.name, window.age) //    张三,18
    sandbox.inactive() //    还原
    console.log(window.name, window.age) //    undefined,undefined
    sandbox.active() //    激活
    console.log(window.name, window.age) //    张三,18
})(sandbox.proxy);

4.2、LegacySandBox

LegacySandbox 是基于 Proxy 的单实例沙箱,当我们只针对全局运行环境进行代理赋值记录,而不从中取值,那么这样的沙箱只是作为我们记录变化的一种手段,而实际操作仍在主应用运行环境中对 window 进行读写,因此这类沙箱也只能支持单实例模式。

//legacySandBox
const callableFnCacheMap = new WeakMap();

function isCallable(fn) {
  if (callableFnCacheMap.has(fn)) {
    return true;
  }
  const naughtySafari = typeof document.all === 'function' && typeof document.all === 'undefined';
  const callable = naughtySafari ? typeof fn === 'function' && typeof fn !== 'undefined' : typeof fn ===
    'function';
  if (callable) {
    callableFnCacheMap.set(fn, callable);
  }
  return callable;
};

function isPropConfigurable(target, prop) {
  const descriptor = Object.getOwnPropertyDescriptor(target, prop);
  return descriptor ? descriptor.configurable : true;
}

function setWindowProp(prop, value, toDelete) {
  if (value === undefined && toDelete) {
    delete window[prop];
  } else if (isPropConfigurable(window, prop) && typeof prop !== 'symbol') {
    Object.defineProperty(window, prop, {
      writable: true,
      configurable: true
    });
    window[prop] = value;
  }
}


function getTargetValue(target, value) {
  /*
    仅绑定 isCallable && !isBoundedFunction && !isConstructable 的函数对象,如 window.console、window.atob 这类。目前没有完美的检测方式,这里通过 prototype 中是否还有可枚举的拓展方法的方式来判断
    @warning 这里不要随意替换成别的判断方式,因为可能触发一些 edge case(比如在 lodash.isFunction 在 iframe 上下文中可能由于调用了 top window 对象触发的安全异常)
   */
  if (isCallable(value) && !isBoundedFunction(value) && !isConstructable(value)) {
    const boundValue = Function.prototype.bind.call(value, target);
    for (const key in value) {
      boundValue[key] = value[key];
    }
    if (value.hasOwnProperty('prototype') && !boundValue.hasOwnProperty('prototype')) {
      Object.defineProperty(boundValue, 'prototype', {
        value: value.prototype,
        enumerable: false,
        writable: true
      });
    }

    return boundValue;
  }

  return value;
}

/**
 * 基于 Proxy 实现的沙箱
 */
class SingularProxySandbox {
  /** 沙箱期间新增的全局变量 */
  addedPropsMapInSandbox = new Map();

  /** 沙箱期间更新的全局变量 */
  modifiedPropsOriginalValueMapInSandbox = new Map();

  /** 持续记录更新的(新增和修改的)全局变量的 map,用于在任意时刻做 snapshot */
  currentUpdatedPropsValueMap = new Map();

  name;

  proxy;

  type = 'LegacyProxy';

  sandboxRunning = true;

  latestSetProp = null;

  active() {
    if (!this.sandboxRunning) {
      this.currentUpdatedPropsValueMap.forEach((v, p) => setWindowProp(p, v));
    }

    this.sandboxRunning = true;
  }

  inactive() {
    // console.log(' this.modifiedPropsOriginalValueMapInSandbox', this.modifiedPropsOriginalValueMapInSandbox)
    // console.log(' this.addedPropsMapInSandbox', this.addedPropsMapInSandbox)
    //删除添加的属性,修改已有的属性
    this.modifiedPropsOriginalValueMapInSandbox.forEach((v, p) => setWindowProp(p, v));
    this.addedPropsMapInSandbox.forEach((_, p) => setWindowProp(p, undefined, true));

    this.sandboxRunning = false;
  }

  constructor(name) {
    this.name = name;
    const {
      addedPropsMapInSandbox,
      modifiedPropsOriginalValueMapInSandbox,
      currentUpdatedPropsValueMap
    } = this;

    const rawWindow = window;
    //Object.create(null)的方式,传入一个不含有原型链的对象
    const fakeWindow = Object.create(null); 

    const proxy = new Proxy(fakeWindow, {
      set: (_, p, value) => {
        if (this.sandboxRunning) {
          if (!rawWindow.hasOwnProperty(p)) {
            addedPropsMapInSandbox.set(p, value);
          } else if (!modifiedPropsOriginalValueMapInSandbox.has(p)) {
            // 如果当前 window 对象存在该属性,且 record map 中未记录过,则记录该属性初始值
            const originalValue = rawWindow[p];
            modifiedPropsOriginalValueMapInSandbox.set(p, originalValue);
          }

          currentUpdatedPropsValueMap.set(p, value);
          // 必须重新设置 window 对象保证下次 get 时能拿到已更新的数据
          rawWindow[p] = value;

          this.latestSetProp = p;

          return true;
        }

        // 在 strict-mode 下,Proxy 的 handler.set 返回 false 会抛出 TypeError,在沙箱卸载的情况下应该忽略错误
        return true;
      },

      get(_, p) {
        //避免使用 window.window 或者 window.self 逃离沙箱环境,触发到真实环境
        if (p === 'top' || p === 'parent' || p === 'window' || p === 'self') {
          return proxy;
        }
        const value = rawWindow[p];
        return getTargetValue(rawWindow, value);
      },

      has(_, p) { //返回boolean
        return p in rawWindow;
      },

      getOwnPropertyDescriptor(_, p) {
        const descriptor = Object.getOwnPropertyDescriptor(rawWindow, p);
        // 如果属性不作为目标对象的自身属性存在,则不能将其设置为不可配置
        if (descriptor && !descriptor.configurable) {
          descriptor.configurable = true;
        }
        return descriptor;
      },
    });

    this.proxy = proxy;
  }
}

let sandbox = new SingularProxySandbox();

((window) => {
  window.name = '张三';
  window.age = 18;
  window.sex = '男';
  console.log(window.name, window.age,window.sex) //    张三,18,男
  sandbox.inactive() //    还原
  console.log(window.name, window.age,window.sex) //    undefined,undefined,undefined
  sandbox.active() //    激活
  console.log(window.name, window.age,window.sex) //    张三,18,男
})(sandbox.proxy); //test

4.3、ProxySandbox

LegacySandbox 由于会修改 window 对象,在多个实例运行时肯定会存在冲突。因此,该沙箱模式只能在单实例场景下使用,而当我们需要同时起多个实例时,ProxySandbox 便登场了。

ProxySandbox 的方案是同时使用 Proxy 给子应用运行环境做了 get 与 set 拦截(对 fakeWindow 进行代理,而这个对象是通过 createFakeWindow 方法创建,这个方法是将 window 的 document、location、top、window 等属性拷贝一份)。沙箱在初始构造是建立一个状态池,当应用操作 window 时,赋值通过 set 拦截器将变量写入状态池,而取值也是从状态池中优先寻找对应的属性。由于状态池与子应用绑定,那么运行多个子应用,便可以产生多个相互独立的沙箱环境。

由于取值赋值均在建立的状态池上操作。因此,在以上两种沙箱环境下激活和卸载需要做的工作,这里就不需要了。

function createFakeWindow(global: Window) {
  // map always has the fastest performance in has check scenario
  // see https://jsperf.com/array-indexof-vs-set-has/23
  const propertiesWithGetter = new Map<PropertyKey, boolean>();
  const fakeWindow = {} as FakeWindow;

  /*
   copy the non-configurable property of global to fakeWindow
   see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/getOwnPropertyDescriptor
   > A property cannot be reported as non-configurable, if it does not exists as an own property of the target object or if it exists as a configurable own property of the target object.
   */
  Object.getOwnPropertyNames(global)
    .filter((p) => {
      const descriptor = Object.getOwnPropertyDescriptor(global, p);
      return !descriptor?.configurable;
    })
    .forEach((p) => {
      const descriptor = Object.getOwnPropertyDescriptor(global, p);
      if (descriptor) {
        const hasGetter = Object.prototype.hasOwnProperty.call(descriptor, 'get');

        /*
         make top/self/window property configurable and writable, otherwise it will cause TypeError while get trap return.
         see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get
         > The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable data property.
         */
        if (
          p === 'top' ||
          p === 'parent' ||
          p === 'self' ||
          p === 'window' ||
          (process.env.NODE_ENV === 'test' && (p === 'mockTop' || p === 'mockSafariTop'))
        ) {
          descriptor.configurable = true;
          /*
           The descriptor of window.window/window.top/window.self in Safari/FF are accessor descriptors, we need to avoid adding a data descriptor while it was
           Example:
            Safari/FF: Object.getOwnPropertyDescriptor(window, 'top') -> {get: function, set: undefined, enumerable: true, configurable: false}
            Chrome: Object.getOwnPropertyDescriptor(window, 'top') -> {value: Window, writable: false, enumerable: true, configurable: false}
           */
          if (!hasGetter) {
            descriptor.writable = true;
          }
        }

        if (hasGetter) propertiesWithGetter.set(p, true);

        // freeze the descriptor to avoid being modified by zone.js
        // see https://github.com/angular/zone.js/blob/a5fe09b0fac27ac5df1fa746042f96f05ccb6a00/lib/browser/define-property.ts#L71
        rawObjectDefineProperty(fakeWindow, p, Object.freeze(descriptor));
      }
    });

  return {
    fakeWindow,
    propertiesWithGetter,
  };
}

5、ShadowRealm

ShadowRealm 是 ECMAScript 标准提案,旨在创建一个独立的全局环境,它的全局对象包含自己的内建函数与对象,有自己独立作用域,方案当前处于 step3 阶段。提案地址:https://github.com/tc39/proposal-shadowrealm

declare class ShadowRealm {
  constructor();
   // 同步执行代码字符串,类似 eval()。
  evaluate(sourceText: string): PrimitiveValueOrCallable;
  // 返回一个 Promise 对象,异步执行代码字符串。
  importValue(specifier: string, bindingName: string): Promise<PrimitiveValueOrCallable>; 
}

const sr = new ShadowRealm();
console.assert(
  sr.evaluate(`'ab' + 'cd'`) === 'abcd'
);

// main.js
const sr = new ShadowRealm();
const wrappedSum = await sr.importValue('./my-module.js', 'sum');
console.assert(wrappedSum('hi', ' ', 'folks', '!') === 'hi ConardLi!');

// my-module.js
export function sum(...values) {
  return values.reduce((prev, value) => prev + value);
}

参考:
import-html-entry

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/532879.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JOSE约瑟 JZS-7E14/11静态可调延时中间继电器 自动控制电路 接通、分断电路】

JZS-7E14/11静态可调延时中间继电器品牌:JOSEF约瑟名称:静态可调延时中间继电器型号:JZS-7E14/11额定电压:6220VDC&#xff1b;6380VAC触点容量:10A/250V10A/220VDC功率消耗:≤6W 一 用途 JZS-7E系列中间继电器用于各种保护和自动控制装置中,以增加保护和控制回路的触点容量. …

Java面试知识点(全)-数据结构和算法

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 基础的数据结构 数组 数组的下标寻址十分迅速&#xff0c;但计算机的内存是有限的&#xff0c;故数组的长度也是有限的&#xff0c;实际应用当中的数据…

伙伴云CEO戴志康:低代码与GPT,是赛车手和领航员的角色

GPT来的突然&#xff0c;不仅打了那些对AI冷眼相待的人们一个措手不及&#xff0c;也顺势带动了全民”AIGC”讨论热潮&#xff0c;让大众开始期待它的到来&#xff0c;能为这个人间添上多少精彩.... 万众期待下&#xff0c;GPT也没谦虚&#xff0c;大笔一挥间便融入了到了协同办…

Java集合常见面试题

1、Java集合概述 Java集合&#xff0c;也叫作容器。由两大接口派生而来&#xff1a;Collection接口&#xff0c;用于存放单一元素&#xff1b;Map接口&#xff0c;主要用于存放键值对。对于Collection接口&#xff0c;下面又有三个主要的子接口&#xff1a;List、Set、Queue 2…

桌面远程工具推荐

目前市面上的远程工具多如牛毛&#xff0c;很多人不知道怎么选择&#xff0c;下面小编介绍两种桌面远程工具&#xff0c;它们都是跨平台的&#xff0c;均支持Windows&#xff0c;Mac OS&#xff0c;IOS和安卓&#xff0c;分别是RayLink&#xff0c;VNC&#xff0c;好用&#xf…

eKuiper 源码解读:从一条 SQL 到流处理任务的旅程

概述 LF Edge eKuiper 是 Golang 实现的轻量级物联网边缘分析、流式处理开源软件&#xff0c;可以运行在各类资源受限的边缘设备上。eKuiper 的主要目标是在边缘端提供一个流媒体软件框架。其规则引擎允许用户提供基于SQL 或基于图形&#xff08;类似于 Node-RED&#xff09;的…

权威硬核认证|数说故事携手IDEA共创学术论文获NLP国际顶会 ACL 2023收录

日前&#xff0c;数说故事携手IDEA共创的学术论文——《A Unified One-Step Solution for Aspect Sentiment Quad Prediction (一个统一的单步情感四元组识别方法) 》被国际学术顶会 ACL 2023 接收为 Findings长文。这是继上一年IDEA数说故事实验室论文获「国际AI顶会IJCAI-ECA…

加密解密软件VMProtect教程(六):主窗口之控制面板“项目”部分(1)

VMProtect 是保护应用程序代码免遭分析和破解的可靠工具&#xff0c;但只有在正确构建应用程序内保护机制并且没有可能破坏整个保护的典型错误的情况下才能最有效地使用。 接下来为大家介绍关于VMProtect主窗口中的控制面板&#xff0c;其中包括&#xff1a;“项目”部分、“功…

AD20 原理图设计流程

Altium Designer 20 的原理图设计大致可以分为9个步骤&#xff1a; &#xff08;1&#xff09;新建原理图。这是原理图设计的第一步。 &#xff08;2&#xff09;图纸设置。图纸设置就是要设置图纸的大小&#xff0c;方向等信息。图纸设置要根据电路图的内容和标准化来进行。…

教你几分钟玩转.ipynb文件

找代码的时候最不喜欢遇到.ipynb文件&#xff0c;因为要打开jupyter&#xff0c;作为懒癌患者&#xff0c;即使电脑安装了jupyter也很少去用。不知道有没有人和我一样&#xff0c;真的很不喜欢在终端开一个程序&#xff0c;不能关的那种。 今天又遇到.ipynb文件&#xff0c;这…

我是如何利用midjourney制作表情包的

起初是在看到大厂文章《【Midjourney教程】设计麻瓜也能10分钟上架一套表情包》以后&#xff0c;才想自己试试的。如果你是midjourney的老鸟了&#xff0c;那么参照着文章&#xff0c;应该也能很顺利的完成。下面我介绍下&#xff0c;我遇到的问题和解决方案 准备&#xff1a;…

Tesseract.js离线识别图片中的文字

从官网下载Tesseract.js的离线版本 https://github.com/jeromewu/tesseract.js-offline 初始化 解压下载文件使用cmd命令行进入解压的文件夹&#xff08;tesseract.js-offline-master&#xff09;&#xff0c;使用命令下载安装相关包npm install下载安装完成后&#xff0c;该…

看懂二维码识别OCR:从算法到API 接入代码

引言 二维码识别OCR&#xff08;Optical Character Recognition&#xff09;是结合了图像处理和OCR技术&#xff0c;以识别和提取二维码中的信息的技术&#xff0c;二维码识别OCR 可以实现对图像中的二维码进行自动检测和解码&#xff0c;并将其内容提取为可编辑的文本&#x…

腾讯云 Serverless Stable Diffusion 应用免费名额限量放送,试用申请开启!

近半年&#xff0c;AIGC 领域惊喜接踵而至。除了 Chatgpt&#xff0c;在AI绘图方面 Stable Diffusion 也大放异彩。网上的教程五花八门&#xff0c;有很多小伙伴根本不知如何下手&#xff0c;苦不堪言。 现在腾讯云 Serverless Stable Diffusion 应用免费名额限量放送&#xf…

阿里P6测试总监分享,这份《接口自动化测试》总结,让我成功入门接口自动化测试...

昨晚在某个测试交流群&#xff0c;听了一个测试老司机分享接口自动化测试的内容&#xff0c;对接口自动化有了更深的一些认识&#xff0c;也为接下来公司的接口自动化实施&#xff0c;提供了更多的思路。 这篇文章&#xff0c;就说说功能测试到接口自动化的进阶&#xff0c;以…

( 位运算 ) 318. 最大单词长度乘积 ——【Leetcode每日一题】

❓318. 最大单词长度乘积 难度&#xff1a;中等 给你一个字符串数组 words &#xff0c;找出并返回 length(words[i]) * length(words[j]) 的最大值&#xff0c;并且这两个单词不含有公共字母。如果不存在这样的两个单词&#xff0c;返回 0 。 示例 1&#xff1a; 输入&…

sqlmap对dvwa靶场的账号密码进行破解

1.进行靶场搭建 准备两台虚拟机 靶机&#xff1a;win7 攻击机&#xff1a;kali linux win7IP 172.26.0.130kali linuxIP 172.26.0.129 虚拟机搭建好后,相互ping能ping同就行 安装xampp XAMPP Installers and Downloads for Apache FriendsXAMPP is an easy to install…

数字化赋能,探索智慧银行建设的最佳实践

导语 | 数字经济时代&#xff0c;数字化已成为银行业转型升级的战略手段。近年来&#xff0c;商业银行纷纷加大对信息科技的投入&#xff0c;数字化在改变银行业务模式的同时&#xff0c;更是构建起了数字金融新生态。今天&#xff0c;我们特邀腾讯云 TVP 行业大使、舜源科技合…

值传递、引用传递

​​​​​辟谣时间 错误理解一&#xff1a;值传递和引用传递&#xff0c;区分的条件是传递的内容&#xff0c;如果是个值&#xff0c;就是值传递。如果是个引用&#xff0c;就是引用传递。 错误理解二&#xff1a;Java是引用传递。 错误理解三&#xff1a;传递的参数如果是普通…

国内有哪些SAAS软件?SAAS软件有哪些优点?

国内有哪些SAAS软件&#xff1f;SAAS软件有哪些优点&#xff1f;不请自来答一下&#xff0c;通过SaaS软件与传统软件的对比来详细讲下SaaS软件有哪些优点&#xff1f; 配合以下内容食用更佳&#xff1a; 关于概念——深度详解什么是SaaS&#xff08;软件即服务&#xff09;关…