diff --git a/.eslintrc.js b/.eslintrc.js index 8243e0fdf1d..5b69afa019f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -84,27 +84,52 @@ module.exports = { files: key + '/**/*.js', rules: { 'prebid/validate-imports': ['error', allowedModules[key]], - 'prebid/no-innerText': ['error', allowedModules[key]], 'no-restricted-globals': [ 'error', { name: 'require', message: 'use import instead' } + ], + 'prebid/no-global': [ + 'error', + ...['localStorage', 'sessionStorage'].map(name => ({name, message: 'use storageManager instead'})), + { + name: 'XMLHttpRequest', + message: 'use ajax.js instead' + }, + ], + 'prebid/no-member': [ + 'error', + { + name: 'cookie', + target: 'document', + message: 'use storageManager instead' + }, + { + name: 'sendBeacon', + target: 'navigator', + message: 'use ajax.js instead' + }, + ...['outerText', 'innerText'].map(name => ({ + name, + message: 'use .textContent instead' + })) ] } })).concat([{ // code in other packages (such as plugins/eslint) is not "seen" by babel and its parser will complain. files: 'plugins/*/**/*.js', parser: 'esprima' - }, - { + }, { files: '**BidAdapter.js', rules: { 'no-restricted-imports': [ 'error', { - patterns: ["**/src/events.js", - "**/src/adloader.js"] + patterns: [ + '**/src/events.js', + '**/src/adloader.js' + ] } ] } diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b225be162a8..367ace94d37 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -41,7 +41,7 @@ For any user facing change, submit a link to a PR on the docs repo at https://gi } ``` -Be sure to test the integration with your adserver using the [Hello World](/integrationExamples/gpt/hello_world.html) sample page. --> +Be sure to test the integration with your adserver using the [Hello World](https://github.com/prebid/Prebid.js/blob/master/integrationExamples/gpt/hello_world.html) sample page. --> ## Other information diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 2e8465003e4..8d3788e8956 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -2,3 +2,6 @@ paths: - src - modules - libraries +queries: + - name: Prebid queries + uses: ./.github/codeql/queries diff --git a/.github/codeql/queries/deviceMemory.ql b/.github/codeql/queries/deviceMemory.ql new file mode 100644 index 00000000000..6f650abf0e1 --- /dev/null +++ b/.github/codeql/queries/deviceMemory.ql @@ -0,0 +1,14 @@ +/** + * @id prebid/device-memory + * @name Access to navigator.deviceMemory + * @kind problem + * @problem.severity warning + * @description Finds uses of deviceMemory + */ + +import prebid + +from SourceNode nav +where + nav = windowPropertyRead("navigator") +select nav.getAPropertyRead("deviceMemory"), "deviceMemory is an indicator of fingerprinting" diff --git a/.github/codeql/queries/hardwareConcurrency.ql b/.github/codeql/queries/hardwareConcurrency.ql new file mode 100644 index 00000000000..350dbd1ae81 --- /dev/null +++ b/.github/codeql/queries/hardwareConcurrency.ql @@ -0,0 +1,14 @@ +/** + * @id prebid/hardware-concurrency + * @name Access to navigator.hardwareConcurrency + * @kind problem + * @problem.severity warning + * @description Finds uses of hardwareConcurrency + */ + +import prebid + +from SourceNode nav +where + nav = windowPropertyRead("navigator") +select nav.getAPropertyRead("hardwareConcurrency"), "hardwareConcurrency is an indicator of fingerprinting" diff --git a/.github/codeql/queries/prebid.qll b/.github/codeql/queries/prebid.qll new file mode 100644 index 00000000000..02fb5adc93c --- /dev/null +++ b/.github/codeql/queries/prebid.qll @@ -0,0 +1,36 @@ +import javascript +import DataFlow + +SourceNode otherWindow() { + result = globalVarRef("top") or + result = globalVarRef("self") or + result = globalVarRef("parent") or + result = globalVarRef("frames").getAPropertyRead() or + result = DOM::documentRef().getAPropertyRead("defaultView") +} + +SourceNode connectedWindow(SourceNode win) { + result = win.getAPropertyRead("self") or + result = win.getAPropertyRead("top") or + result = win.getAPropertyRead("parent") or + result = win.getAPropertyRead("frames").getAPropertyRead() or + result = win.getAPropertyRead("document").getAPropertyRead("defaultView") +} + +SourceNode relatedWindow(SourceNode win) { + result = connectedWindow(win) or + result = relatedWindow+(connectedWindow(win)) +} + +SourceNode anyWindow() { + result = otherWindow() or + result = relatedWindow(otherWindow()) +} + +/* + Matches uses of property `prop` done on any window object. +*/ +SourceNode windowPropertyRead(string prop) { + result = globalVarRef(prop) or + result = anyWindow().getAPropertyRead(prop) +} diff --git a/.github/codeql/queries/qlpack.yml b/.github/codeql/queries/qlpack.yml new file mode 100644 index 00000000000..72e90d3de9b --- /dev/null +++ b/.github/codeql/queries/qlpack.yml @@ -0,0 +1,8 @@ +--- +library: false +warnOnImplicitThis: false +name: queries +version: 0.0.1 +dependencies: + codeql/javascript-all: ^1.1.1 + codeql/javascript-queries: ^1.1.0 diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index a3246cffd6d..5876dfa0138 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,6 +1,10 @@ name-template: 'Prebid $RESOLVED_VERSION Release' tag-template: '$RESOLVED_VERSION' +autolabeler: + - label: 'maintenance' + title: + - '/^(?!.*(bug|initial|release|fix)).*$/i' categories: - title: '🚀 New Features' label: 'feature' diff --git a/.github/workflows/jscpd.yml b/.github/workflows/jscpd.yml index 21e7aadf97c..de5f1408dff 100644 --- a/.github/workflows/jscpd.yml +++ b/.github/workflows/jscpd.yml @@ -29,7 +29,7 @@ jobs: run: | echo '{ "threshold": 20, - "minTokens": 50, + "minTokens": 100, "reporters": [ "json" ], @@ -101,7 +101,7 @@ jobs: const filteredReport = JSON.parse(fs.readFileSync('filtered-jscpd-report.json', 'utf8')); let comment = "Whoa there, partner! 🌵🤠 We wrangled some duplicated code in your PR:\n\n"; function link(dup) { - return `https://github.com/${{ github.event.repository.full_name }}/blob/${{ github.event.pull_request.head.sha }}/${dup.name}#L${dup.start}-L${dup.end - 1}` + return `https://github.com/${{ github.event.repository.full_name }}/blob/${{ github.event.pull_request.head.sha }}/${dup.name}#L${dup.start + 1}-L${dup.end - 1}` } filteredReport.forEach(duplication => { const firstFile = duplication.firstFile; @@ -109,7 +109,7 @@ jobs: const lines = duplication.lines; comment += `- [\`${firstFile.name}\`](${link(firstFile)}) has ${lines} duplicated lines with [\`${secondFile.name}\`](${link(secondFile)})\n`; }); - comment += "\nReducing code duplication by importing common functions from a library not only makes our code cleaner but also easier to maintain. Please move the common code from both files into a library and import it in each. Keep up the great work! 🚀"; + comment += "\nReducing code duplication by importing common functions from a library not only makes our code cleaner but also easier to maintain. Please move the common code from both files into a library and import it in each. We hate that we have to mention this, however, commits designed to hide from this utility by renaming variables or reordering an object are poor conduct. We will not look upon them kindly! Keep up the great work! 🚀"; github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 00000000000..03ef6478f1c --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,110 @@ +name: Check for linter warnings / exceptions + +on: + pull_request_target: + branches: + - master + +jobs: + check-linter: + runs-on: ubuntu-latest + + steps: + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.base.sha }} + + - name: Fetch base and target branches + run: | + git fetch origin +refs/heads/${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }} + git fetch origin +refs/pull/${{ github.event.pull_request.number }}/merge:refs/remotes/pull/${{ github.event.pull_request.number }}/merge + + - name: Install dependencies + run: npm ci + + - name: Get the diff + run: git diff --name-only origin/${{ github.event.pull_request.base.ref }}...refs/remotes/pull/${{ github.event.pull_request.number }}/merge | grep '^\(modules\|src\|libraries\|creative\)/.*\.js$' > __changed_files.txt || true + + - name: Run linter on base branch + run: npx eslint --no-inline-config --format json $(cat __changed_files.txt | xargs stat --printf '%n\n' 2> /dev/null) > __base.json || true + + - name: Check out PR + run: git checkout ${{ github.event.pull_request.head.sha }} + + - name: Install dependencies + run: npm ci + + - name: Run linter on PR + run: npx eslint --no-inline-config --format json $(cat __changed_files.txt | xargs stat --printf '%n\n' 2> /dev/null) > __pr.json || true + + - name: Compare them and post comment if necessary + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + const process = require('process'); + + function parse(fn) { + return JSON.parse(fs.readFileSync(fn)).reduce((memo, data) => { + const file = path.relative(process.cwd(), data.filePath); + if (!memo.hasOwnProperty(file)) { memo[file] = { errors: 0, warnings: 0} } + data.messages.forEach(({severity}) => { + memo[file][severity > 1 ? 'errors' : 'warnings']++; + }); + return memo; + }, {}) + } + + function mkDiff(old, new_) { + const files = Object.fromEntries( + Object.entries(new_) + .map(([file, {errors, warnings}]) => { + const {errors: oldErrors, warnings: oldWarnings} = old[file] || {}; + return [file, {errors: Math.max(0, errors - (oldErrors ?? 0)), warnings: Math.max(0, warnings - (oldWarnings ?? 0))}] + }) + .filter(([_, {errors, warnings}]) => errors > 0 || warnings > 0) + ) + return Object.values(files).reduce((memo, {warnings, errors}) => { + memo.errors += errors; + memo.warnings += warnings; + return memo; + }, {errors: 0, warnings: 0, files}) + } + + function mkComment({errors, warnings, files}) { + function pl(noun, number) { + return noun + (number === 1 ? '' : 's') + } + if (errors === 0 && warnings === 0) return; + const summary = []; + if (errors) summary.push(`**${errors}** linter ${pl('error', errors)}`) + if (warnings) summary.push(`**${warnings}** linter ${pl('warning', warnings)}`) + let cm = `Tread carefully! This PR adds ${summary.join(' and ')} (possibly disabled through directives):\n\n`; + Object.entries(files).forEach(([file, {errors, warnings}]) => { + const summary = []; + if (errors) summary.push(`+${errors} ${pl('error', errors)}`); + if (warnings) summary.push(`+${warnings} ${pl('warning', warnings)}`) + cm += ` * \`${file}\` (${summary.join(', ')})\n` + }) + return cm; + } + + const [base, pr] = ['__base.json', '__pr.json'].map(parse); + const comment = mkComment(mkDiff(base, pr)); + + if (comment) { + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); + } diff --git a/README.md b/README.md index f890f055104..65dc668164e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Build Status](https://circleci.com/gh/prebid/Prebid.js.svg?style=svg)](https://circleci.com/gh/prebid/Prebid.js) -[![Percentage of issues still open](http://isitmaintained.com/badge/open/prebid/Prebid.js.svg)](http://isitmaintained.com/project/prebid/Prebid.js "Percentage of issues still open") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/prebid/Prebid.js.svg)](https://isitmaintained.com/project/prebid/Prebid.js "Percentage of issues still open") [![Coverage Status](https://coveralls.io/repos/github/prebid/Prebid.js/badge.svg)](https://coveralls.io/github/prebid/Prebid.js) # Prebid.js @@ -7,8 +7,8 @@ > A free and open source library for publishers to quickly implement header bidding. This README is for developers who want to contribute to Prebid.js. -Additional documentation can be found at [the Prebid homepage](http://prebid.org). -Working examples can be found in [the developer docs](http://prebid.org/dev-docs/getting-started.html). +Additional documentation can be found at [the Prebid.js documentation homepage](https://docs.prebid.org/prebid/prebidjs.html). +Working examples can be found in [the developer docs](https://prebid.org/dev-docs/getting-started.html). Prebid.js is open source software that is offered for free as a convenience. While it is designed to help companies address legal requirements associated with header bidding, we cannot and do not warrant that your use of Prebid.js will satisfy legal requirements. You are solely responsible for ensuring that your use of Prebid.js complies with all applicable laws. We strongly encourage you to obtain legal advice when using Prebid.js to ensure your implementation complies with all laws where you operate. @@ -26,7 +26,7 @@ Prebid.js is open source software that is offered for free as a convenience. Whi *Note:* Requires Prebid.js v1.38.0+ -Prebid.js depends on Babel and some Babel Plugins in order to run correctly in the browser. Here are some examples for +Prebid.js depends on Babel and some Babel Plugins in order to run correctly in the browser. Here are some examples for configuring webpack to work with Prebid.js. With Babel 7: @@ -37,7 +37,7 @@ module.exports = { mode: 'production', module: { rules: [ - + // this rule can be excluded if you don't require babel-loader for your other application files { test: /\.m?js$/, @@ -46,7 +46,7 @@ module.exports = { loader: 'babel-loader', } }, - + // this separate rule is required to make sure that the Prebid.js files are babel-ified. this rule will // override the regular exclusion from above (for being inside node_modules). { @@ -71,7 +71,7 @@ Or for Babel 6: // you must manually install and specify the presets and plugins yourself options: { plugins: [ - "transform-object-assign", // required (for IE support) and "babel-plugin-transform-object-assign" + "transform-object-assign", // required (for IE support) and "babel-plugin-transform-object-assign" // must be installed as part of your package. require('prebid.js/plugins/pbjsGlobals.js') // required! ], @@ -79,7 +79,7 @@ Or for Babel 6: ["env", { // you can use other presets if you wish. "targets": { // this example is using "babel-presets-env", which must be installed if you "browsers": [ // follow this example. - ... // your browser targets. they should probably match the targets you're using for the rest + ... // your browser targets. they should probably match the targets you're using for the rest // of your application ] } @@ -143,7 +143,7 @@ This will run testing but not linting. A web server will start at `http://localh Development may be a bit slower but if you prefer linting and additional watch files you can also still run just: - $ gulp serve + $ gulp serve ### Build Optimization @@ -162,11 +162,11 @@ Building with just these adapters will result in a smaller bundle which should a - Then run the build: $ gulp build --modules=openxBidAdapter,rubiconBidAdapter,sovrnBidAdapter - + Alternatively, a `.json` file can be specified that contains a list of modules you would like to include. $ gulp build --modules=modules.json - + With `modules.json` containing the following ```json modules.json [ @@ -202,7 +202,7 @@ gulp bundle --tag one --modules=one.json gulp bundle --tag two --modules=two.json ``` -This generates slightly larger files, but has the advantage of being much faster to run (after the initial `gulp build`). It's also the method used by [the Prebid.org download page](https://docs.prebid.org/download.html). +This generates slightly larger files, but has the advantage of being much faster to run (after the initial `gulp build`). It's also the method used by [the Prebid.org download page](https://docs.prebid.org/download.html). @@ -374,11 +374,11 @@ The results will be in *Note*: Starting in June 2016, all pull requests to Prebid.js need to include tests with greater than 80% code coverage before they can be merged. For more information, see [#421](https://github.com/prebid/Prebid.js/issues/421). -For instructions on writing tests for Prebid.js, see [Testing Prebid.js](http://prebid.org/dev-docs/testing-prebid.html). +For instructions on writing tests for Prebid.js, see [Testing Prebid.js](https://prebid.org/dev-docs/testing-prebid.html). ### Supported Browsers -Prebid.js is supported on IE11 and modern browsers until 5.x. 6.x+ transpiles to target >0.25%; not Opera Mini; not IE11. +Prebid.js is supported on IE11 and modern browsers until 5.x. 6.x+ transpiles to target >0.25%; not Opera Mini; not IE11. ### Governance Review our governance model [here](https://github.com/prebid/Prebid.js/tree/master/governance.md). diff --git a/creative/crossDomain.js b/creative/crossDomain.js index 5799b817aa5..d3524f61d4b 100644 --- a/creative/crossDomain.js +++ b/creative/crossDomain.js @@ -32,10 +32,13 @@ function isPrebidWindow(win) { export function renderer(win) { let target = win.parent; - while (target !== win.top && !isPrebidWindow(target)) { - target = target.parent; + try { + while (target !== win.top && !isPrebidWindow(target)) { + target = target.parent; + } + if (!isPrebidWindow(target)) target = win.parent; + } catch (e) { } - if (!isPrebidWindow(target)) target = win.parent; return function ({adId, pubUrl, clickUrl}) { const pubDomain = new URL(pubUrl, window.location).origin; diff --git a/integrationExamples/gpt/51DegreesRtdProvider_example.html b/integrationExamples/gpt/51DegreesRtdProvider_example.html index 5f66dd9c8e2..7864f2e05f5 100644 --- a/integrationExamples/gpt/51DegreesRtdProvider_example.html +++ b/integrationExamples/gpt/51DegreesRtdProvider_example.html @@ -92,7 +92,7 @@ name: '51Degrees', waitForIt: true, params: { - // Get your resource key from https://configure.51degrees.com/tWrhNfY6 + // Get your resource key from https://configure.51degrees.com/HNZ75HT1 resourceKey: '', // alternatively, you can use the on-premise version of the 51Degrees service and connect to your chosen end point // onPremiseJSUrl: 'https://localhost/51Degrees.core.js' @@ -181,12 +181,11 @@

div-banner-native-2

Testing/Debugging Guidance

  1. Make sure you have debug: true under pbjs.setConfig in this example code (be sure to remove it for production!) -
  2. Make sure you have replaced <YOUR RESOURCE KEY> in this example code with the one you have obtained - from the 51Degrees Configurator Tool
  3. +
  4. Make sure you have replaced <YOUR RESOURCE KEY> in this example code with the one you have obtained + from the 51Degrees Configurator Tool
  5. Open DevTools Console in your browser and refresh the page
  6. Observe the enriched ortb device data shown below and also in the console as part of the [51Degrees RTD Submodule]: reqBidsConfigObj: message (under reqBidsConfigObj.global.device)
-