Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

webrtc-adapter

Package Overview
Dependencies
Maintainers
4
Versions
120
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webrtc-adapter - npm Package Compare versions

Comparing version 7.2.1 to 7.2.2

1

dist/adapter_factory.js

@@ -82,2 +82,3 @@ 'use strict';

chromeShim.shimGetSendersWithDtmf(window);
chromeShim.shimGetStats(window);
chromeShim.shimSenderReceiverGetStats(window);

@@ -84,0 +85,0 @@ chromeShim.fixNegotiationNeeded(window);

225

dist/chrome/chrome_shim.js

@@ -39,2 +39,3 @@

exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
exports.shimGetStats = shimGetStats;
exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;

@@ -52,42 +53,2 @@ exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;

/* iterates the stats graph recursively. */
function walkStats(stats, base, resultSet) {
if (!base || resultSet.has(base.id)) {
return;
}
resultSet.set(base.id, base);
Object.keys(base).forEach(function (name) {
if (name.endsWith('Id')) {
walkStats(stats, stats.get(base[name]), resultSet);
} else if (name.endsWith('Ids')) {
base[name].forEach(function (id) {
walkStats(stats, stats.get(id), resultSet);
});
}
});
}
/* filter getStats for a sender/receiver track. */
function filterStats(result, track, outbound) {
var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
var filteredResult = new Map();
if (track === null) {
return filteredResult;
}
var trackStats = [];
result.forEach(function (value) {
if (value.type === 'track' && value.trackIdentifier === track.id) {
trackStats.push(value);
}
});
trackStats.forEach(function (trackStat) {
result.forEach(function (stats) {
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
walkStats(result, stats, filteredResult);
}
});
});
return filteredResult;
}
function shimMediaStream(window) {

@@ -272,2 +233,70 @@ window.MediaStream = window.MediaStream || window.webkitMediaStream;

function shimGetStats(window) {
if (!window.RTCPeerConnection) {
return;
}
var origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function (selector, successCallback, errorCallback) {
var _this5 = this;
var args = arguments;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats.apply(this, arguments);
}
// When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
if (origGetStats.length === 0 && (arguments.length === 0 || typeof arguments[0] !== 'function')) {
return origGetStats.apply(this, []);
}
var fixChromeStats_ = function fixChromeStats_(response) {
var standardReport = {};
var reports = response.result();
reports.forEach(function (report) {
var standardStats = {
id: report.id,
timestamp: report.timestamp,
type: {
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[report.type] || report.type
};
report.names().forEach(function (name) {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
};
// shim getStats with maplike support
var makeMapStats = function makeMapStats(stats) {
return new Map(Object.keys(stats).map(function (key) {
return [key, stats[key]];
}));
};
if (arguments.length >= 2) {
var successCallbackWrapper_ = function successCallbackWrapper_(response) {
args[1](makeMapStats(fixChromeStats_(response)));
};
return origGetStats.apply(this, [successCallbackWrapper_, arguments[0]]);
}
// promise-support
return new Promise(function (resolve, reject) {
origGetStats.apply(_this5, [function (response) {
resolve(makeMapStats(fixChromeStats_(response)));
}, reject]);
}).then(successCallback, errorCallback);
};
}
function shimSenderReceiverGetStats(window) {

@@ -283,7 +312,7 @@ if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {

window.RTCPeerConnection.prototype.getSenders = function () {
var _this5 = this;
var _this6 = this;
var senders = origGetSenders.apply(this, []);
senders.forEach(function (sender) {
return sender._pc = _this5;
return sender._pc = _this6;
});

@@ -310,3 +339,3 @@ return senders;

*/
filterStats(result, sender.track, true)
utils.filterStats(result, sender.track, true)
);

@@ -322,7 +351,7 @@ });

window.RTCPeerConnection.prototype.getReceivers = function () {
var _this6 = this;
var _this7 = this;
var receivers = origGetReceivers.apply(this, []);
receivers.forEach(function (receiver) {
return receiver._pc = _this6;
return receiver._pc = _this7;
});

@@ -339,3 +368,3 @@ return receivers;

return this._pc.getStats().then(function (result) {
return filterStats(result, receiver.track, false);
return utils.filterStats(result, receiver.track, false);
});

@@ -394,7 +423,7 @@ };

window.RTCPeerConnection.prototype.getLocalStreams = function () {
var _this7 = this;
var _this8 = this;
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
return Object.keys(this._shimmedLocalStreams).map(function (streamId) {
return _this7._shimmedLocalStreams[streamId][0];
return _this8._shimmedLocalStreams[streamId][0];
});

@@ -421,3 +450,3 @@ };

window.RTCPeerConnection.prototype.addStream = function (stream) {
var _this8 = this;
var _this9 = this;

@@ -427,3 +456,3 @@ this._shimmedLocalStreams = this._shimmedLocalStreams || {};

stream.getTracks().forEach(function (track) {
var alreadyExists = _this8.getSenders().find(function (s) {
var alreadyExists = _this9.getSenders().find(function (s) {
return s.track === track;

@@ -452,3 +481,3 @@ });

window.RTCPeerConnection.prototype.removeTrack = function (sender) {
var _this9 = this;
var _this10 = this;

@@ -458,8 +487,8 @@ this._shimmedLocalStreams = this._shimmedLocalStreams || {};

Object.keys(this._shimmedLocalStreams).forEach(function (streamId) {
var idx = _this9._shimmedLocalStreams[streamId].indexOf(sender);
var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender);
if (idx !== -1) {
_this9._shimmedLocalStreams[streamId].splice(idx, 1);
_this10._shimmedLocalStreams[streamId].splice(idx, 1);
}
if (_this9._shimmedLocalStreams[streamId].length === 1) {
delete _this9._shimmedLocalStreams[streamId];
if (_this10._shimmedLocalStreams[streamId].length === 1) {
delete _this10._shimmedLocalStreams[streamId];
}

@@ -486,3 +515,3 @@ });

window.RTCPeerConnection.prototype.getLocalStreams = function () {
var _this10 = this;
var _this11 = this;

@@ -492,3 +521,3 @@ var nativeStreams = origGetLocalStreams.apply(this);

return nativeStreams.map(function (stream) {
return _this10._reverseStreams[stream.id];
return _this11._reverseStreams[stream.id];
});

@@ -499,3 +528,3 @@ };

window.RTCPeerConnection.prototype.addStream = function (stream) {
var _this11 = this;
var _this12 = this;

@@ -506,3 +535,3 @@ this._streams = this._streams || {};

stream.getTracks().forEach(function (track) {
var alreadyExists = _this11.getSenders().find(function (s) {
var alreadyExists = _this12.getSenders().find(function (s) {
return s.track === track;

@@ -536,3 +565,3 @@ });

window.RTCPeerConnection.prototype.addTrack = function (track, stream) {
var _this12 = this;
var _this13 = this;

@@ -570,3 +599,3 @@ if (this.signalingState === 'closed') {

Promise.resolve().then(function () {
_this12.dispatchEvent(new Event('negotiationneeded'));
_this13.dispatchEvent(new Event('negotiationneeded'));
});

@@ -613,3 +642,3 @@ } else {

window.RTCPeerConnection.prototype[method] = function () {
var _this13 = this;
var _this14 = this;

@@ -620,3 +649,3 @@ var args = arguments;

return nativeMethod.apply(this, [function (description) {
var desc = replaceInternalStreamId(_this13, description);
var desc = replaceInternalStreamId(_this14, description);
args[0].apply(null, [desc]);

@@ -630,3 +659,3 @@ }, function (err) {

return nativeMethod.apply(this, arguments).then(function (description) {
return replaceInternalStreamId(_this13, description);
return replaceInternalStreamId(_this14, description);
});

@@ -659,3 +688,3 @@ };

window.RTCPeerConnection.prototype.removeTrack = function (sender) {
var _this14 = this;
var _this15 = this;

@@ -679,7 +708,7 @@ if (this.signalingState === 'closed') {

Object.keys(this._streams).forEach(function (streamid) {
var hasTrack = _this14._streams[streamid].getTracks().find(function (track) {
var hasTrack = _this15._streams[streamid].getTracks().find(function (track) {
return sender.track === track;
});
if (hasTrack) {
stream = _this14._streams[streamid];
stream = _this15._streams[streamid];
}

@@ -711,64 +740,2 @@ });

var origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function (selector, successCallback, errorCallback) {
var _this15 = this;
var args = arguments;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats.apply(this, arguments);
}
// When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
if (origGetStats.length === 0 && (arguments.length === 0 || typeof arguments[0] !== 'function')) {
return origGetStats.apply(this, []);
}
var fixChromeStats_ = function fixChromeStats_(response) {
var standardReport = {};
var reports = response.result();
reports.forEach(function (report) {
var standardStats = {
id: report.id,
timestamp: report.timestamp,
type: {
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[report.type] || report.type
};
report.names().forEach(function (name) {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
};
// shim getStats with maplike support
var makeMapStats = function makeMapStats(stats) {
return new Map(Object.keys(stats).map(function (key) {
return [key, stats[key]];
}));
};
if (arguments.length >= 2) {
var successCallbackWrapper_ = function successCallbackWrapper_(response) {
args[1](makeMapStats(fixChromeStats_(response)));
};
return origGetStats.apply(this, [successCallbackWrapper_, arguments[0]]);
}
// promise-support
return new Promise(function (resolve, reject) {
origGetStats.apply(_this15, [function (response) {
resolve(makeMapStats(fixChromeStats_(response)));
}, reject]);
}).then(successCallback, errorCallback);
};
// shim implicit creation of RTCSessionDescription/RTCIceCandidate

@@ -775,0 +742,0 @@ ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {

@@ -187,18 +187,20 @@ /*

// constraints.
var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function (cs) {
return shimConstraints_(cs, function (c) {
return origGetUserMedia(c).then(function (stream) {
if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(function (track) {
track.stop();
});
throw new DOMException('', 'NotFoundError');
}
return stream;
}, function (e) {
return Promise.reject(shimError_(e));
if (navigator.mediaDevices.getUserMedia) {
var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function (cs) {
return shimConstraints_(cs, function (c) {
return origGetUserMedia(c).then(function (stream) {
if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(function (track) {
track.stop();
});
throw new DOMException('', 'NotFoundError');
}
return stream;
}, function (e) {
return Promise.reject(shimError_(e));
});
});
});
};
};
}
}

@@ -99,2 +99,5 @@ /*

var sctpInDescription = function sctpInDescription(description) {
if (!description || !description.sdp) {
return false;
}
var sections = _sdp2.default.splitSections(description.sdp);

@@ -101,0 +104,0 @@ sections.shift();

@@ -25,2 +25,4 @@ /*

exports.compactObject = compactObject;
exports.walkStats = walkStats;
exports.filterStats = filterStats;

@@ -205,1 +207,41 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

}
/* iterates the stats graph recursively. */
function walkStats(stats, base, resultSet) {
if (!base || resultSet.has(base.id)) {
return;
}
resultSet.set(base.id, base);
Object.keys(base).forEach(function (name) {
if (name.endsWith('Id')) {
walkStats(stats, stats.get(base[name]), resultSet);
} else if (name.endsWith('Ids')) {
base[name].forEach(function (id) {
walkStats(stats, stats.get(id), resultSet);
});
}
});
}
/* filter getStats for a sender/receiver track. */
function filterStats(result, track, outbound) {
var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
var filteredResult = new Map();
if (track === null) {
return filteredResult;
}
var trackStats = [];
result.forEach(function (value) {
if (value.type === 'track' && value.trackIdentifier === track.id) {
trackStats.push(value);
}
});
trackStats.forEach(function (trackStat) {
result.forEach(function (stats) {
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
walkStats(result, stats, filteredResult);
}
});
});
return filteredResult;
}
{
"name": "webrtc-adapter",
"version": "7.2.1",
"version": "7.2.2",
"description": "A shim to insulate apps from WebRTC spec changes and browser prefix differences",

@@ -37,3 +37,3 @@ "license": "BSD-3-Clause",

"chai": "^3.5.0",
"grunt": "^1.0.3",
"grunt": "^1.0.4",
"grunt-babel": "^7.0.0",

@@ -40,0 +40,0 @@ "grunt-browserify": "^5.3.0",

@@ -54,2 +54,3 @@ /*

chromeShim.shimGetSendersWithDtmf(window);
chromeShim.shimGetStats(window);
chromeShim.shimSenderReceiverGetStats(window);

@@ -56,0 +57,0 @@ chromeShim.fixNegotiationNeeded(window);

@@ -13,43 +13,2 @@

/* iterates the stats graph recursively. */
function walkStats(stats, base, resultSet) {
if (!base || resultSet.has(base.id)) {
return;
}
resultSet.set(base.id, base);
Object.keys(base).forEach(name => {
if (name.endsWith('Id')) {
walkStats(stats, stats.get(base[name]), resultSet);
} else if (name.endsWith('Ids')) {
base[name].forEach(id => {
walkStats(stats, stats.get(id), resultSet);
});
}
});
}
/* filter getStats for a sender/receiver track. */
function filterStats(result, track, outbound) {
const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
const filteredResult = new Map();
if (track === null) {
return filteredResult;
}
const trackStats = [];
result.forEach(value => {
if (value.type === 'track' &&
value.trackIdentifier === track.id) {
trackStats.push(value);
}
});
trackStats.forEach(trackStat => {
result.forEach(stats => {
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
walkStats(result, stats, filteredResult);
}
});
});
return filteredResult;
}
export {shimGetUserMedia} from './getusermedia';

@@ -230,2 +189,70 @@ export {shimGetDisplayMedia} from './getdisplaymedia';

export function shimGetStats(window) {
if (!window.RTCPeerConnection) {
return;
}
const origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function(selector,
successCallback, errorCallback) {
const args = arguments;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats.apply(this, arguments);
}
// When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
if (origGetStats.length === 0 && (arguments.length === 0 ||
typeof arguments[0] !== 'function')) {
return origGetStats.apply(this, []);
}
const fixChromeStats_ = function(response) {
const standardReport = {};
const reports = response.result();
reports.forEach(report => {
const standardStats = {
id: report.id,
timestamp: report.timestamp,
type: {
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[report.type] || report.type
};
report.names().forEach(name => {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
};
// shim getStats with maplike support
const makeMapStats = function(stats) {
return new Map(Object.keys(stats).map(key => [key, stats[key]]));
};
if (arguments.length >= 2) {
const successCallbackWrapper_ = function(response) {
args[1](makeMapStats(fixChromeStats_(response)));
};
return origGetStats.apply(this, [successCallbackWrapper_,
arguments[0]]);
}
// promise-support
return new Promise((resolve, reject) => {
origGetStats.apply(this, [
function(response) {
resolve(makeMapStats(fixChromeStats_(response)));
}, reject]);
}).then(successCallback, errorCallback);
};
}
export function shimSenderReceiverGetStats(window) {

@@ -263,3 +290,3 @@ if (!(typeof window === 'object' && window.RTCPeerConnection &&

*/
filterStats(result, sender.track, true));
utils.filterStats(result, sender.track, true));
};

@@ -285,3 +312,3 @@ }

return this._pc.getStats().then(result =>
filterStats(result, receiver.track, false));
utils.filterStats(result, receiver.track, false));
};

@@ -639,64 +666,2 @@ }

const origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function(selector,
successCallback, errorCallback) {
const args = arguments;
// If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats.apply(this, arguments);
}
// When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
if (origGetStats.length === 0 && (arguments.length === 0 ||
typeof arguments[0] !== 'function')) {
return origGetStats.apply(this, []);
}
const fixChromeStats_ = function(response) {
const standardReport = {};
const reports = response.result();
reports.forEach(report => {
const standardStats = {
id: report.id,
timestamp: report.timestamp,
type: {
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[report.type] || report.type
};
report.names().forEach(name => {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
};
// shim getStats with maplike support
const makeMapStats = function(stats) {
return new Map(Object.keys(stats).map(key => [key, stats[key]]));
};
if (arguments.length >= 2) {
const successCallbackWrapper_ = function(response) {
args[1](makeMapStats(fixChromeStats_(response)));
};
return origGetStats.apply(this, [successCallbackWrapper_,
arguments[0]]);
}
// promise-support
return new Promise((resolve, reject) => {
origGetStats.apply(this, [
function(response) {
resolve(makeMapStats(fixChromeStats_(response)));
}, reject]);
}).then(successCallback, errorCallback);
};
// shim implicit creation of RTCSessionDescription/RTCIceCandidate

@@ -703,0 +668,0 @@ ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']

@@ -174,16 +174,18 @@ /*

// constraints.
const origGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(cs) {
return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {
if (c.audio && !stream.getAudioTracks().length ||
c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(track => {
track.stop();
});
throw new DOMException('', 'NotFoundError');
}
return stream;
}, e => Promise.reject(shimError_(e))));
};
if (navigator.mediaDevices.getUserMedia) {
const origGetUserMedia = navigator.mediaDevices.getUserMedia.
bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function(cs) {
return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {
if (c.audio && !stream.getAudioTracks().length ||
c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(track => {
track.stop();
});
throw new DOMException('', 'NotFoundError');
}
return stream;
}, e => Promise.reject(shimError_(e))));
};
}
}

@@ -81,2 +81,5 @@ /*

const sctpInDescription = function(description) {
if (!description || !description.sdp) {
return false;
}
const sections = SDPUtils.splitSections(description.sdp);

@@ -83,0 +86,0 @@ sections.shift();

@@ -196,1 +196,43 @@ /*

}
/* iterates the stats graph recursively. */
export function walkStats(stats, base, resultSet) {
if (!base || resultSet.has(base.id)) {
return;
}
resultSet.set(base.id, base);
Object.keys(base).forEach(name => {
if (name.endsWith('Id')) {
walkStats(stats, stats.get(base[name]), resultSet);
} else if (name.endsWith('Ids')) {
base[name].forEach(id => {
walkStats(stats, stats.get(id), resultSet);
});
}
});
}
/* filter getStats for a sender/receiver track. */
export function filterStats(result, track, outbound) {
const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
const filteredResult = new Map();
if (track === null) {
return filteredResult;
}
const trackStats = [];
result.forEach(value => {
if (value.type === 'track' &&
value.trackIdentifier === track.id) {
trackStats.push(value);
}
});
trackStats.forEach(trackStat => {
result.forEach(stats => {
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
walkStats(result, stats, filteredResult);
}
});
});
return filteredResult;
}

@@ -18,10 +18,10 @@ /*

it('the onicecandidate callback', (done) => {
let hasProperty = false;
let hasAddress = false;
const pc = new window.RTCPeerConnection();
pc.onicecandidate = (e) => {
if (!e.candidate) {
expect(hasProperty).to.equal(true);
expect(hasAddress).to.equal(true);
done();
} else {
hasProperty = e.candidate.hasOwnProperty('address');
hasAddress = !!e.candidate.address;
}

@@ -34,10 +34,10 @@ };

it('the icecandidate event', (done) => {
let hasProperty = false;
let hasAddress = false;
const pc = new window.RTCPeerConnection();
pc.addEventListener('icecandidate', (e) => {
if (!e.candidate) {
expect(hasProperty).to.equal(true);
expect(hasAddress).to.equal(true);
done();
} else {
hasProperty = e.candidate.hasOwnProperty('port');
hasAddress = !!e.candidate.address;
}

@@ -44,0 +44,0 @@ });

@@ -67,3 +67,3 @@ /*

};
shim.shimPeerConnection(window);
shim.shimGetStats(window);
pc = new window.RTCPeerConnection();

@@ -70,0 +70,0 @@ });

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc