I have registered for a course that has roughly 150 videos.
What I have done Uptil NOW:
There is no download button available right now.
In order to get the URL of each video file, I have created the script which I run through Console as below:
The site where I am watching these videos is different than the xxxxx marked site.
e.g. I am watching on linkedin learning and video is on lynda,etc.
console.log(("<h2>"+ document.title)+"</h2>"
+
" click here ");
document.getElementsByClassName("video-next-button")[0].click();
an example of output from above code is:
<h2>Overview of QGIS features: Learning QGIS (2015)</h2>
<a href="https://files3.xxxxx.com/secure/courses/383524/VBR_MP4h264_main_SD/383524_01_01_XR15_Overview.mp4?V0lIWk4afWPs3ejN5lxsCi1SIkGKYcNR_F7ijKuQhDmS1sYUK7Ps5TYBcV-MHzdVTujT5p03HP10F_kqzhwhqi38fhOAPnNJz-dMyvA2-YIpBOI-wGtuOjItlVbRUDn6QUWpwe1sRoAl__IA1zmJn3gPvC7Fu926GViqVdLa3oLB0mxRGa7i> click here </a>
I have replaced domain name with xxxxx
This way I can get cover all videos without clicking next (I would like to know if I can automate this process by using some timeout techniques as well)
each of this link, when clicked, chrome window looks like below:
this way after clicking 3dots -> Download, I can save video individually.
What I want:
Method to save all videos without the need to open individually.
Challenge
To begin with, fetching and saving large binary files is possible when:
The host server's CORS support is enabled.
Accessing the host's network from the same site-origin.
Server-to-Server.
Okay, this would reason why your anchor attempt did not work, in fact, accessing the host's network from your localhost will deny you from accessing the resource's content unless the host server's CORS support is enabled which is unlikely.
Workaround
Alternatively, this will leave us with the other two options, accessing from the same site-origin in particular due to its simplicity, the strategy lies in executing the fetching/saving script from the browser itself, thus, the host server will be gentle with the requests, since they are very similar to the ones coming from the same site.
Steps
Go to the site you wish to download the files from (I used https://www.sample-videos.com).
Right-click the web page and select 'Inspect' (Ctrl + Shift + I).
Finally, switch to the 'Console' tab to start coding.
Code
const downloadVideos = (videos, marker) => {
// it's important to throttle between requests to dodge performance or network issues
const throttleTime = 10000; // in milliseconds; adjust it to suit your hardware/network capabilities
const domain = 'https://www.sample-videos.com'; // site's domain
if (marker < videos.length) {
console.log(`Download initiated for video ${videos[marker].name} # marker:${marker}`);
const anchorElement = document.createElement('a');
anchorElement.setAttribute('href', `${domain}${videos[marker].src}`);
anchorElement.setAttribute('download', videos[marker].name);
document.body.appendChild(anchorElement);
// trigger download manually
anchorElement.click();
anchorElement.remove();
marker += 1;
setTimeout(downloadVideos, throttleTime, videos, marker);
}
};
// assuming all videos are stored in an array, each video must have 'src' and 'name' attributes
const videos = [
{ src: '/video123/mp4/480/big_buck_bunny_480p_30mb.mp4', name: 'video_480p.mp4' },
{ src: '/video123/mp4/720/big_buck_bunny_720p_1mb.mp4', name: 'video_720p.mp4' }
];
// fireup
downloadVideos(videos, 0);
... ahem!
I'm trying to trigger some sort of Folder Selection Dialog, I have a working model with nodejs and the powershell but it only works when the server and client are on the same machine. I need the prompt to occur on the client side triggered from the browser. From what i understand I can not trigger Powershell from Chrome? So is there an alternative or am i just screwed?
My current Powershell script
{
param([string]$Description="Select Folder",[string]$RootFolder="Desktop")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$objForm = New-Object System.Windows.Forms.FolderBrowserDialog
$objForm.Rootfolder = $RootFolder
$objForm.Description = $Description
$Show = $objForm.ShowDialog()
If ($Show -eq "OK")
{
Return $objForm.SelectedPath
}
Else
{
Write-Error "Operation cancelled by user."
}
}
$folder = Select-FolderDialog # the variable contains user folder selection
write-host $folder
My javascript function
async function asyncfindDir() {
//executes powershell script
let promise = new Promise((resolve, reject) => {
const Shell = require('node-powershell');
const ps = new Shell({
executionPolicy: 'Bypass',
noProfile: true
});
ps.addCommand('./selectfolder.ps1');
ps.invoke()
.then(output => {
//console.log(output);
var shelloutput = output;
console.log (shelloutput + '^^from external script');
res.send(shelloutput);
})
.catch(err => {
console.log('please select a directory path')
//console.log('err');
});
});
};
Is there anyway to get that working locally?
Is there a trigger i'm not aware of to access that kind of dialog from the browser? I know i'm not the only person with this issue but i have yet to see a real solution.
Short answer: No.
Longer answer, is best illustrated by rephrasing your question with a different script name:
Using my browser, can I click on a link to visit a website, and have it run a random
PowerShell script called Delete_All_Files.ps1?
Answers why you will never be able to run a PowerShell script from a browser, on a remote machine, and why browsers will deliberately block you from doing it, because people usually don't want to have all their files deleted when they click on a random link in their email.
If you want to run PowerShell scripts on remote machines, then you should look into PSRemoting and Enter-PSSession.
#kuzimoto is right. If you just want to display a folder dialog box, there are easier ways to do that and Fine Uploader is an easier way.
Replying to your comment: If you want to specify a directory name, the reason you can't do it is because you are essentially asking:
Using my browser, can I click on a link to visit a website, and have
it run a script that will enumerate through all the files and folders
in my C:\ so that it can choose the folder C:\users\Justin
Miller\Desktop\SECRET FILES\?
The reason both operations do not work is because both operations require local computer access. i.e. local script execution access, and local directory knowledge access. Security-wize, we, in general, don't want to visit a random website and have it execute random code, or know what files/folders I have on my machine, which is why you won't be able to do what you want to try to do.
I'm coding a script in nodejs to automatically retrieve data from an online directory.
Knowing that I had never done this, I chose javascript because it is a language I use every day.
I therefore from the few tips I could find on google use request with cheerios to easily access components of dom of the page.
I found and retrieved all the necessary information, the only missing step is to recover the link to the next page except that the one is generated 4 seconds after loading of page and link contains a hash so that this step Is unavoidable.
What I would like to do is to recover dom of page 4-5 seconds after its loading to be able to recover the link
I looked on the internet, and much advice to use PhantomJS for this manipulation, but I can not get it to work after many attempts with node.
This is my code :
#!/usr/bin/env node
require('babel-register');
import request from 'request'
import cheerio from 'cheerio'
import phantom from 'node-phantom'
phantom.create(function(err,ph) {
return ph.createPage(function(err,page) {
return page.open(url, function(err,status) {
console.log("opened site? ", status);
page.includeJs('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function(err) {
//jQuery Loaded.
//Wait for a bit for AJAX content to load on the page. Here, we are waiting 5 seconds.
setTimeout(function() {
return page.evaluate(function() {
var tt = cheerio.load($this.html())
console.log(tt)
}, function(err,result) {
console.log(result);
ph.exit();
});
}, 5000);
});
});
});
});
but i get this error :
return ph.createPage(function (page) {
^
TypeError: ph.createPage is not a function
Is what I am about to do is the best way to do what I want to do? If not what is the simplest way? If so, where does my error come from?
If You dont have to use phantomjs You can use nightmare to do it.
It is pretty neat library to solve problems like yours, it uses electron as web browser and You can run it with or without showing window (You can also open developer tools like in Google Chrome)
It has only one flaw if You want to run it on server without graphical interface that You must install at least framebuffer.
Nightmare has method like wait(cssSelector) that will wait until some element appears on website.
Your code would be something like:
const Nightmare = require('nightmare');
const nightmare = Nightmare({
show: true, // will show browser window
openDevTools: true // will open dev tools in browser window
});
const url = 'http://hakier.pl';
const selector = '#someElementSelectorWitchWillAppearAfterSomeDelay';
nightmare
.goto(url)
.wait(selector)
.evaluate(selector => {
return {
nextPage: document.querySelector(selector).getAttribute('href')
};
}, selector)
.then(extracted => {
console.log(extracted.nextPage); //Your extracted data from evaluate
});
//this variable will be injected into evaluate callback
//it is required to inject required variables like this,
// because You have different - browser scope inside this
// callback and You will not has access to node.js variables not injected
Happy hacking!
I just updated a website I'm working on, unfortunately a few links I didn't create are popping up in one of the corners. I could really use a bit of guidance as to how I should go about cleaning the site and removing all of it.
The only files I uploaded to the server were a handful of bootstrap css files, the index.html, and one image for a background. I've run aVast on all these files but they're coming up clean, although I'm not sure if this is a sufficient enough scan.
All files were uploaded using filezilla FTP.
I've opened up inspect element in chrome while loading the webpage and under resources it shows files being loaded that aren't mine and that I can't locate on the server. To be specific, one is an image file whose URL points to acint(dot)net and the other is a script called aci.js, which is located at acint(dot)net/aci.js (code below)
(function(n){n(window,"undefined"===typeof window._acic?{}:window._acic,"undefined"===typeof window._aci_debug?!1:window._aci_debug)})(function(n,f,t){function k(){if(!(this instanceof k))return new k;this.version="0.0.9";this.urlHit="//www.acint.net/hit/";this.urlJump="//www.acint.net/jump/";this.uid="";this.config={dataProvider:"",allowExtLinksTrack:!0,customData:null}}var v=!!t,s={};"object"==typeof JSON&&"function"==typeof JSON.stringify?s.stringify=function(a){return JSON.stringify(a)}:function(){function a(a){function b(a){return 10>
a?"0"+a:a}if(a&&"object"==typeof a){if(a instanceof Date)return isFinite(a.valueOf())?a.getUTCFullYear()+"-"+b(a.getUTCMonth()+1)+"-"+b(a.getUTCDate())+"T"+b(a.getUTCHours())+":"+b(a.getUTCMinutes())+":"+b(a.getUTCSeconds())+"Z":null;if(a instanceof String||a instanceof Number||a instanceof Boolean)return a.valueOf()}return a}function c(a){e.lastIndex=0;return e.test(a)?'"'+a.replace(e,function(a){var b=h[a];return"string"===typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+
a+'"'}function d(e,u){var h,r,f,k,n=b,p,m=u[e],m=a(m);"function"===typeof q&&(m=q.call(u,e,m));switch(typeof m){case "string":return c(m);case "number":return isFinite(m)?String(m):"null";case "boolean":case "null":return String(m);case "object":if(!m)return"null";b+=g;p=[];if("[object Array]"===Object.prototype.toString.apply(m)){k=m.length;for(h=0;h<k;h+=1)p[h]=d(h,m)||"null";f=0===p.length?"[]":b?"[\n"+b+p.join(",\n"+b)+"\n"+n+"]":"["+p.join(",")+"]";b=n;return f}if(q&&"object"===typeof q)for(k=
q.length,h=0;h<k;h+=1)r=q[h],"string"===typeof r&&(f=d(r,m))&&p.push(c(r)+(b?": ":":")+f);else for(r in m)Object.prototype.hasOwnProperty.call(m,r)&&(f=d(r,m))&&p.push(c(r)+(b?": ":":")+f);f=0===p.length?"{}":b?"{\n"+b+p.join(",\n"+b)+"\n"+n+"}":"{"+p.join(",")+"}";b=n;return f}}var e,b,g,h,q;"function"!==typeof s.stringify&&(e=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,h={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",
'"':'\\"',"\\":"\\\\"},s.stringify=function(a,c,e){var h;g=b="";if("number"===typeof e)for(h=0;h<e;h+=1)g+=" ";else"string"===typeof e&&(g=e);if((q=c)&&"function"!==typeof c&&("object"!==typeof c||"number"!==typeof c.length))throw Error("JSONStub.stringify");return d("",{"":a})})}();var e={generateUUID:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var c=16*Math.random()|0;return("x"==a?c:c&3|8).toString(16)}).toLowerCase()},stringTrimLimit:function(a){a=a.replace(/\s+/gmi,
" ").replace(/^\s+|\s+$/g,"");1E3<a.length&&(a=a.substr(0,1E3));return a},getTimeZoneOffsetIso8601:function(){var a=function(a){return 10>a?"0"+a:a},c=(new Date).getTimezoneOffset();return(0<c?"-":"+")+a(Math.floor(Math.abs(c)/60))+":"+a(Math.abs(c)%60)},isObject:function(a){return"object"===typeof a&&"[object Object]"===Object.prototype.toString.call(a)},isArray:function(a){return"undefined"!==typeof Array.isArray?Array.isArray(a):"[object Array]"===Object.prototype.toString.call(a)},isEmptyObject:function(a){for(var c in a)return!1;
return!0},isString:function(a){return"string"===typeof a||a instanceof String},encodeUriParam:function(a){a=""+a;return encodeURIComponent instanceof Function?encodeURIComponent(a):escape(a)},implodeUriParams:function(a){var c,d=[];for(c in a)a.hasOwnProperty(c)&&d.push(c+"="+a[c]);return d.join("&")},makeRequestUri:function(a,c){return a+"?"+e.implodeUriParams(c)},makeRequestImage:function(a){(new Image).src=a},addEventListenerCrossBrowser:function(a,c,d){a.addEventListener?a.addEventListener(c,
d,!1):a.attachEvent?a.attachEvent("on"+c,d):"function"==typeof a["on"+c]&&(a["on"+c]=d)},addReadOnlyProperty:function(a,c,d){Object.defineProperty(a,c,{value:d,writeable:!1,enumerable:!0,configurable:!1})},fireEventCrossBrowser:function(a,c){var d,e=document;if(e.createEvent){var b=null;switch(c){case "click":case "dblclick":case "mousedown":case "mouseup":case "mouseover":case "mousemove":case "mouseout":case "mouseenter":case "mouseleave":b="MouseEvent";break;case "wheel":b="WheelEvent";break;case "load":case "unload":case "abort":case "error":case "select":case "resize":case "scroll":b=
"UIEvent";break;case "focus":case "focusin":case "focusout":case "blur":b="FocusEvent";break;case "beforeinput":case "input":b="InputEvent";break;case "keydown":case "keyup":b="KeyboardEvent";break;case "compositionstart":case "compositionupdate":case "compositionend":b="CompositionEvent"}try{d=e.createEvent(b)}catch(g){try{d=e.createEvent("HtmlEvents")}catch(h){try{d=e.createEvent("Event")}catch(q){throw Error("Cannot create event object for specified event: "+c);}}}d.initEvent(c,!0,!1);a.dispatchEvent(d)}else if(e.createEventObject)d=
e.createEventObject(),d.eventType=c,a.fireEvent("on"+c,d);else if("function"==typeof a["on"+c])a["on"+c]()},fireSameEventCrosBrowser:function(a,c,d){var e=document,b;if(e.createEvent)try{b=new d.constructor(c,d),a.dispatchEvent(b)}catch(g){b=e.createEvent("MouseEvent"),b.initMouseEvent(d.type,!0,!0,window,0,d.screenX,d.screenY,d.clientX,d.clientY,d.ctrlKey,d.altKey,d.shiftKey,d.metaKey,d.button,d.relatedTarget||null),a.dispatchEvent(b)}else if(e.createEventObject)b=e.createEventObject(d),b.eventType=
c,a.fireEvent("on"+c,b);else if("function"==typeof a["on"+c])a["on"+c]()},bindOnReady:function(a){function c(){e||(e=!0,a())}function d(){if(!e)try{h.doScroll("left"),c()}catch(a){setTimeout(d,10)}}var e=!1,b=window,g=document,h=g.documentElement;if(g.addEventListener)g.addEventListener("DOMContentLoaded",c,!1);else if(g.attachEvent){try{var q=null!=b.frameElement}catch(l){}h.doScroll&&!q&&d();g.attachEvent("onreadystatechange",function(){"complete"===g.readyState&&c()})}else if(b.addEventListener)b.addEventListener("load",
c,!1);else if(b.attachEvent)b.attachEvent("onload",c);else{var f=b.onload;b.onload=function(){f&&f();c()}}}};k.prototype.init=function(){if(!this.isAlreadyLoaded()){var a=this,c,d=document;this.uid=e.generateUUID();this.parseConfig();c=e.makeRequestUri(this.urlHit,this.collectDataOnInit());e.makeRequestImage(c);if(!0===this.config.allowExtLinksTrack){var f=function(b){var d=window,c=!1,f;b=b||d.event;var l=b.target||b.srcElement,k=l;if(!("tagName"in l&&"a"==l.tagName.toLowerCase())){for(c=l.parentNode;c;){if("tagName"in
c&&"a"==c.tagName.toLowerCase()){f=c;break}if("parentNode"in c)c=c.parentNode;else break}if(f)l=f;else return}if("href"in l&&/^(http:|https:|)\/\/.+/.test(l.href)&&!("hostname"in l&&l.hostname===d.location.hostname)){if(b.shiftKey||b.altKey||b.ctrlKey||b.metaKey)c=!0;c=!0;l.hasOwnProperty("_delayClick")?!1===l.hasOwnProperty("_canSkipDelay")?"preventDefault"in b?b.preventDefault():b.returnValue=!1:(delete l._canSkipDelay,delete l._delayClick):(d=e.makeRequestUri(a.urlJump,a.collectDataOnClick(b,l)),
e.makeRequestImage(d),!1===c&&(l._delayClick=!0,setTimeout(function(){l._canSkipDelay=!0;e.fireSameEventCrosBrowser(k,"click",b)},200),"preventDefault"in b?b.preventDefault():b.returnValue=!1))}};"interactive"==d.readyState||"complete"==d.readyState?e.addEventListenerCrossBrowser(d.body,"click",f):e.bindOnReady(function(){e.addEventListenerCrossBrowser(d.body,"click",f)})}}};k.prototype.isAlreadyLoaded=function(){if("object"==typeof n._acil&&"function"==typeof n._acil.isLoaded)return!0;"undefined"!==
typeof Object.defineProperty?(e.addReadOnlyProperty(n,"_acil",{}),e.addReadOnlyProperty(n._acil,"isLoaded",function(){return!0})):n._acil=function(){return{isLoaded:function(){return!0}}}();return!1};k.prototype.parseConfig=function(){if(e.isObject(f)&&!e.isEmptyObject(f)&&(f.hasOwnProperty("dataProvider")&&(this.config.dataProvider=isNaN(parseInt(f.dataProvider,10))?"":Math.abs(parseInt(f.dataProvider,10))),f.hasOwnProperty("allowExtLinksTrack")&&(this.config.allowExtLinksTrack=!0===f.allowExtLinksTrack),
f.hasOwnProperty("customData"))){var a=f.customData;if(!("undefined"===typeof a||null==a||"function"==typeof a||e.isString(a)&&0===a.length||e.isObject(a)&&e.isEmptyObject(a)||e.isArray(a)&&0===a.length)){try{if(e.isObject(a)||e.isArray(a)){if(a=s.stringify(a),"{}"===a||"[]"===a)a=null}else e.isString(a)||(a=a.toString(),0===a.length&&(a=null))}catch(c){if(a=null,!0===t)throw c;}this.config.customData=a}}};k.prototype.dataAddObligatoryParams=function(a){a.v=this.version;a.uid=this.uid;a.dp=this.config.dataProvider;
a.tz=e.encodeUriParam(e.getTimeZoneOffsetIso8601());a.nc=Math.random().toString().substr(2,8)};k.prototype.collectDataOnInit=function(){var a=document,c=window,d={u:e.encodeUriParam(c.location.href),r:e.encodeUriParam(a.referrer||""),rs:c.screen.width+"x"+c.screen.height,t:e.encodeUriParam(e.stringTrimLimit(a.title)),oE:+this.config.allowExtLinksTrack};null!==this.config.customData&&(d.cd=e.encodeUriParam(e.stringTrimLimit(this.config.customData)));c.parent!==c&&(d["if"]=e.encodeUriParam(c.location.href),
d.u=e.encodeUriParam(a.referrer||""),d.r="");this.dataAddObligatoryParams(d);return d};k.prototype.collectDataOnClick=function(a,c){var d=document,f=window,b={vP:"",c:"",r:"",u:"",aT:"",hT:0},g=a.target.ownerDocument||d,d=g.documentElement,g=g.body;b.vP=(f.innerWidth||d.clientWidth||g.clientWidth)+"x"+(f.innerHeight||d.clientHeight||g.clientHeight);null==a.pageX&&null!=a.clientX?b.c=a.clientX+(d&&d.scrollLeft||g&&g.scrollLeft||0)-(d&&d.clientLeft||g&&g.clientLeft||0)+"x"+(a.clientY+(d&&d.scrollTop||
g&&g.scrollTop||0)-(d&&d.clientTop||g&&g.clientTop||0)):null!==a.pageX&&(b.c=a.pageX+"x"+a.pageY);b.r=e.encodeUriParam(f.location.href);b.u=e.encodeUriParam(c.href);var f=c.childNodes,h=0,k=0,l=0;if(1<=f.length){for(d=0;d<f.length;d++)g=f[d],3!==g.nodeType&&1!==g.nodeType||1!==g.nodeType||(k+=1,"tagName"in f[d]&&"img"===f[d].tagName.toLowerCase()&&(h+=1,l=d));1==h&&h==k?b.hT=1:0<k&&(b.hT=2)}switch(b.hT){case 1:b.aT=f[l].getAttribute("alt")||"";break;case 2:case 0:b.aT=c.innerHTML?c.innerHTML.toString().replace(/<\/?[^>]+>/gmi,
""):""}0<b.aT.length&&(b.aT=e.stringTrimLimit(b.aT),""!==b.aT&&(b.aT=/^(\S+(\s|)){1,10}/gmi.exec(b.aT)[0].replace(/^\s+|\s+$/g,"")));b.aT=e.encodeUriParam(b.aT);this.dataAddObligatoryParams(b);return b};try{(new k).init()}catch(w){if(!0===v)throw w;}});
Any help would be really appreciated!
Some security weakness in your website is allowing someone to inject malicious scripts and/or code.
Disable the web server while you work on resolving this issue.
Change your FTP password.
If you can, switch to SFTP and use a certificate for authentication instead of a password.
Set the firewall to only accept FTP connections from your address, if that IP address is static (will not change on you), or from a range of IP addresses that you may be assigned by your ISP if you can determine such a range.
Remove all of your website code and re-upload from source control.
If you have a database behind your code:
Review your code for SQL Injection attack opportunities
Manually review all text columns in all tables for anything that looks like JavaScript.
Note: Since aVast did not turn up problems, the above steps are likely to get you back to where you need to be. If the problem persists, you may need to perform a complete reinstall of your server.
If you disable your extensions does it still show up? It is possible , if it isn't within the source code itself (like if the source code on the website is the same as on your computer) the script is an ads/monetizing script added by an extension like Freecorder. This is especially suspicious of the extension adding the script is an adware such as RelevantKnowledge, Yontoo, Iminent or iNTERNETTurbo.
If disabling your extensions/using a different browser makes no difference, some webhosts such as 1freehosting will add a script to your page for analytical purposes to check if people still visit your site and/or give you a graph of how many visits you've gotten. The script allows them to count the calls to the server for that page. This is not within your control - the webhost itself adds that code after the end html tag.
If neither it is possible your FTP has been accessed and you should change your password.
My wordpress websites were becoming slow and I've found out the same code was infecting my websites.
The malware javascript is loaded from http://www.acint.net/aci.js and because of it it takes about 20 seconds to fully load the page.
The malware creating a gif file which contains some script, it's located in "wp-content/themes/(your theme name)/images" and it's called logos.gif or logos2.gif - if you go to that location with your FTP client and sort files by most recently created it should be on the top of the list. I think it's created every time a home page is opened.
Make sure your wordpress is up to date (make files and database backup before updating) - that should remove the malware, also after that remove the logos.gif file.
https://sitecheck.sucuri.net/ - that's a cool free scanner to check if your website is infected.
I use this plugin for signing in but it didn't help in that case:
https://wordpress.org/plugins/are-you-robot-recaptcha/
I've installed this Firewall plugin so hopefully it'll make the website more secure:
https://wordpress.org/plugins/wp-simple-firewall/