Software error while executing CGI script - html

I have a cgi script for upload which is as follows
#!/usr/bin/perl
use CGI;
use CGI::Carp qw(fatalsToBrowser);
my $cgi = new CGI;
my $file = $cgi->param('file');
$file=~m/^.*(\\|\/)(.*)/; # strip the remote path and keep the filename
my $name = $2;
open(LOCAL, ">/home/Desktop/$name") or die $!;
while(<$file>) {
$data .= $_;
}
print $cgi->header();
print "$file has been successfully uploaded... thank you.\n";
print $data;
The HTML file is as follows
<html>
<head>
<title>Test</title>
</head>
<body>
<form enctype="multipart/form-data" action="upload.cgi" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
Send this file: <input name="userfile" type="file" />
<input type="submit" value="Send File" />
</form>
</body>
</html>
I am getting a weird error now..
Software error:
Is a directory at htdocs/upload.cgi line 9.
For help, please send mail to this site's webmaster, giving this error message and the time and date of the error.

TL;DR
Stop. Shelve your script right now. It is a gaping security hole just waiting to be exploited. Read the following resources:
perlsec
the CERT Perl Secure Coding Standard
(particularly the section on Input Validation and Data Sanitization)
the OWASP page on Unrestricted File Upload
the InfoSec page on Complete File Upload Vulnerabilities
the CWE page on Unrestricted Upload of File with Dangerous Type
the SANS recommendations for 8 Basic Rules to Implement Secure File Uploads.
When you have read--and understood--all of them, stop and think if you really need to let users upload files onto your server. Think long and hard. Can you really account for all of the listed vulnerabilities? If you still feel like you need to do this, consider enlisting the help of a security expert. Follow the guidelines laid out in the above resources carefully and understand that a mistake in your design could compromise your entire site.
I understand that this is just a test script, not a production application (at least, I really hope that's the case), but even so, what you are doing (and particularly how you are doing it) is a very, very bad idea. Here are a select few of
the reasons why, from OWASP's page on Unrestricted File
Upload:
The website can be defaced.
The web server can be compromised by uploading and executing a web-shell which can: run a command, browse the system files, browse the local resources, attack to other servers, and exploit the local vulnerabilities, and so on.
This vulnerability can make the website vulnerable to some other types of attacks such as XSS.
Local file inclusion vulnerabilities can be exploited by uploading a malicious file into the server.
More from OWASP:
Uploaded files represent a significant risk to applications. The first step in
many attacks is to get some code to the system to be attacked. Then the attack
only needs to find a way to get the code executed. Using a file upload helps
the attacker accomplish the first step.
The consequences of unrestricted file upload can vary, including complete
system takeover, an overloaded file system, forwarding attacks to backend
systems, and simple defacement.
Pretty scary stuff, huh?
The problems
Your code
Let's start by looking at some of the problems with the code you posted.
No strict, no warnings
Start putting use strict; use warnings; at the top of every Perl script you
ever write. I recently had the pleasure of fixing a CGI script that contained
a snippet something like this:
my ($match) = grep { /$usrname/ } #users;
This code was used to check that the username entered in an HTML form matched a
list of valid users. One problem: the variable $usrname was
misspelled (it should have been $username with an 'e'). Since strict
checking was off, Perl happily inserted the value of the (undeclared) global
variable $usrname, or undef. That turned the innocent-looking snippet into this monstrosity:
my ($match) = grep { // } #users;
which matches everything in the valid users list and returns the first
match. You could enter anything you wanted into the username field in the form
and the script would think you were a valid user. Since warnings were also off,
this was never caught during the development process. When you turn warnings on,
the script will still run and return a user, but you also get something like
this:
Name "main::usrname" used only once: possible typo at -e line 1.
Use of uninitialized value $usrname in regexp compilation at -e line 1.
When you also turn on strict, the script fails to compile and won't even run at
all. There are other problems with this snippet (for example, the string 'a' will match the username 'janedoe'), but strict and warnings at least alerted us to one major issue. I cannot stress this enough: always, always use strict; use
warnings;
No taint mode
The first rule of web development is,
"Always sanitize user input." Repeat after me: Always sanitize user input. One more time: Always sanitize user input.
In other words, never
blindly trust user input without validating it first. Users (even those that are not malicious) are very good at entering creative values into form
fields that can break your application (or worse). If you don't restrict their creativity,
there is no limit to the damage a malicious user can do to your site (refer to the perennial #1
vulnerability on the OWASP Top 10,
injection).
Perl's taint mode can help with this. Taint mode forces you
to check all user input before using it in certain potentially dangerous operations like the
system() function. Taint mode is like the safety on a gun: it can prevent a lot of painful
accidents (although if you really want to shoot yourself in the foot, you can
always turn off the safety, like when you untaint a variable without actually removing dangerous characters).
Turn on taint mode in every CGI script you ever write. You can enable it by passing the -T flag, like this:
#!/usr/bin/perl -T
Once taint mode is enabled, your script will throw a fatal error if you try to
use tainted data in dangerous situations. Here's an example of such a dangerous situation that I found in a random script on the internet:
open(LOCAL, ">/home/Desktop/$name") or die $!;
Ok, I lied, that snippet isn't from a random script, it's from your code. In isolation, this snippet is just begging to be hit with a directory traversal attack, where a malicious user enters a relative path in order to access a file that they shouldn't have access to.
Fortunately, you've done something right here: you ensured that $name will contain no directory separators by using a regex*. This is exactly what taint mode would require you to do. The benefit of taint mode is that if you forget to sanitize your input, you will be alerted immediately with an error like this:
Insecure dependency in open while running with -T switch at foo.cgi line 5
Like strict, taint mode forces you to address problems in your code immediately by causing the program to fail, instead of allowing it to quietly limp along.
* You did something right, but you also did some things wrong:
Your program will die if the user passes in only a filename with no directory separators, e.g. foo
You don't remove special characters that could be interpreted by a shell, like |
You never sanitize the variable $file and yet you try to use it to read a file later in your code
You don't check if the file you're writing to already exists (see "No check for file existence" below)
You allow the user to choose the name of the file that will be stored on your server, which gives them far more control than you should be comfortable with (see "Allowing the user to set the file name" below)
CGI::Carp fatalsToBrowser
I'll give you the benefit of the doubt on this one since you're still testing your script, but just in case you weren't aware and since I'm already talking about CGI security issues, never enable CGI::Carp's fatalsToBrowser option in a production environment. It can reveal intimate details about the inner workings of your script to attackers.
Two-argument open() and global filehandles
Two-argument open(), e.g.
open FH, ">$file"
has a host of security risks associated with it when users are allowed to specify the file path. Your script mitigates many of these by using a hard-coded directory prefix, but that in no way diminishes the fact that using two-argument open can be very dangerous. In general, you should use the three-argument form:
open my $fh, ">", $file
(which is still plenty dangerous if you allow the user to specify the file name; see "Allowing the user to set the file name" below).
Also note that instead of the global filehandle FH I switched to a lexical filehandle $fh. See CERT's page Do not use bareword filehandles for some reasons why.
No check for file existence
You don't check whether a file already exists at /home/Desktop/$name when you open it for writing. If the file already exists, you will truncate it (erase its contents) as soon as the open() call succeeds, even if you never write anything to the file. Users (malicious and otherwise) are likely to clobber each other's files, which doesn't make for a very happy user base.
No limit on file size
"But wait," you say, "I set MAX_FILE_SIZE in my HTML form!" Understand that this is merely a suggestion to the browser; attackers can easily edit HTTP requests to remove this condition. Never rely on hidden HTML fields for security. Hidden fields are plainly visible in the HTML source of your page and in the raw HTTP requests. You must limit the maximum request size on the server side to prevent users from loading massive files to your server and to help alleviate one type of denial of service attack. Set the $CGI::POST_MAX variable at the beginning of your CGI script like this:
$CGI::POST_MAX=1024 * 30; # 30KB
Or even better, find CGI.pm on your system and change the value of $POST_MAX to set it globally for all scripts that use the CGI module. That way you don't have to remember to set the variable at the beginning of every CGI script you write.
CGI doesn't match the HTML form
The POST variable you use for the file path in your HTML form, userfile, does not match the variable you look for in your CGI script, file. This is why your script is failing with the error
Is a directory
The value of
$cgi->param('file')
is undef so your script tries to open the path
/home/Desktop/
as a regular file.
Obsolete method for handling upload
You are using the old (and obsolete) method of handling uploads with CGI.pm where param() is used to get both the file name and a lightweight filehandle. This will not work with strict and is insecure. The upload() method was added in v2.47 (all the way back in 1999!) as a preferred replacement. Use it like this (straight out of the documentation for CGI.pm):
$lightweight_fh = $q->upload('field_name');
# undef may be returned if it's not a valid file handle
if (defined $lightweight_fh) {
# Upgrade the handle to one compatible with IO::Handle:
my $io_handle = $lightweight_fh->handle;
open (OUTFILE,'>>','/usr/local/web/users/feedback');
while ($bytesread = $io_handle->read($buffer,1024)) {
print OUTFILE $buffer;
}
}
where field_name is the name of the POST variable that holds the file name (in your case, userfile). Notice that the sample code does not set the output filename based on user input, which leads to my next point.
Allowing the user to set the file name
Never allow users to choose the file name that will be used on your server. If an attacker can upload a malicious file to a known location, it becomes significantly easier for them to exploit. Instead, generate a new, unique (to prevent clobbering), difficult-to-guess file name, preferably in a path outside your web root so users cannot access them directly with a URL.
Other issues
You haven't even begun to address the following issues.
Authentication
Who is allowed to upload files using your web app? How will you ensure that only authorized users are uploading files?
Access control
Are users allowed to see the files uploaded by other users? Depending on the file content, there could be major privacy issues at stake.
Number and rate of uploads
How many files is one user allowed to upload? How many files is a user allowed to upload in a fixed period of time? If you don't restrict these, one user could easily eat up all of your server resources very quickly, even if you enforce a maximum file size.
Dangerous file types
How will you check that users are not uploading dangerous content (for example, executable PHP code) to your server? Simply checking the file extension or content type header is not enough; attackers have found some very creative methods for circumventing such checks.
"But, but, I'm only running this on my corporate intranet..."
You may be tempted to disregard these security issues if your script is not accessible from the internet. However, you still need to consider
In-office pranksters
Disgruntled coworkers
Collaborators and outside contractors who either need access to your app or who shouldn't have access
Managers who love your app so much that they decide to open it up to users on the internet without your knowledge, possibly after you've transferred to another group or left the company
"What should I do?"
Scrap your existing code. Read the resources I listed in the first paragraph, carefully. Here they are again:
perlsec
the CERT Perl Secure Coding Standard (particularly the section on Input Validation and Data
Sanitization)
OWASP's Unrestricted File Upload
InfoSec's Complete File Upload Vulnerabilities
CWE's Unrestricted Upload of File with Dangerous Type
SANS recommendations for 8 Basic Rules to Implement Secure File Uploads
Consider carefully if you really need to do this. If you just need to give users a place to store files, consider using (S)FTP instead. This would certainly not eliminate all of the security risks, but it would eliminate a big one: your custom CGI code.
If after careful consideration you still think this is necessary, work through some recent Perl tutorials to make sure you can use and understand modern Perl programming conventions. Instead of CGI.pm, use a framework like Catalyst, Dancer, or Mojolicious, all of which have plugins that can handle tricky areas like user authentication and sessions so you don't have to re-invent the wheel (poorly).
Follow all of the security guidelines listed in the above resources and consider enlisting the help of an expert in web security. Tread carefully: a single mistake in your code could allow an attacker to compromise your entire site and possibly even other machines on your network. Depending on what country your company and your users are in, this could even have legal ramifications.
</soapbox>

A few suggestions that might get you closer to a solution.
use strict and use warnings (always use these).
CGI->new() instead of new CGI (not essential, but a good habit to get into).
Your file upload form input is called "userfile", but in your CGI code you call it "file". That inconsistency needs to be fixed.
You get the filename with $cgi->param('file'), that gets you the filename correctly (well, once you've fixed the error in my previous point), but you later try to treat that filename as a file handle (while (<$file>)). That's not going to work.
You should probably read the documentation about how to process a file upload field using CGI.pm.

Related

Apache forbidden error when trying to run a Perl script [duplicate]

I have a Perl CGI script that isn't working and I don't know how to start narrowing down the problem. What can I do?
Note: I'm adding the question because I really want to add my very lengthy answer to Stack Overflow. I keep externally linking to it in other answers and it deserves to be here. Don't be shy about editing my answer if you have something to add.
This answer is intended as a general framework for working through
problems with Perl CGI scripts and originally appeared on Perlmonks as Troubleshooting Perl CGI Scripts. It is not a complete guide to every
problem that you may encounter, nor a tutorial on bug squashing. It
is just the culmination of my experience debugging CGI scripts for twenty (plus!) years. This page seems to have had many different homes, and I seem
to forget it exists, so I'm adding it to the StackOverflow. You
can send any comments or suggestions to me at
bdfoy#cpan.org. It's also community wiki, but don't go too nuts. :)
Are you using Perl's built in features to help you find problems?
Turn on warnings to let Perl warn you about questionable parts of your code. You can do this from the command line with the -w switch so you don't have to change any code or add a pragma to every file:
% perl -w program.pl
However, you should force yourself to always clear up questionable code by adding the warnings pragma to all of your files:
use warnings;
If you need more information than the short warning message, use the diagnostics pragma to get more information, or look in the perldiag documentation:
use diagnostics;
Did you output a valid CGI header first?
The server is expecting the first output from a CGI script to be the CGI header. Typically that might be as simple as print "Content-type: text/plain\n\n"; or with CGI.pm and its derivatives, print header(). Some servers are sensitive to error output (on STDERR) showing up before standard output (on STDOUT).
Try sending errors to the browser
Add this line
use CGI::Carp 'fatalsToBrowser';
to your script. This also sends compilation errors to the browser window. Be sure to remove this before moving to a production environment, as the extra information can be a security risk.
What did the error log say?
Servers keep error logs (or they should, at least).
Error output from the server and from your script should
show up there. Find the error log and see what it says.
There isn't a standard place for log files. Look in the
server configuration for their location, or ask the server
admin. You can also use tools such as CGI::Carp
to keep your own log files.
What are the script's permissions?
If you see errors like "Permission denied" or "Method not
implemented", it probably means that your script is not
readable and executable by the web server user. On flavors
of Unix, changing the mode to 755 is recommended:
chmod 755 filename. Never set a mode to 777!
Are you using use strict?
Remember that Perl automatically creates variables when
you first use them. This is a feature, but sometimes can
cause bugs if you mistype a variable name. The pragma
use strict will help you find those sorts of
errors. It's annoying until you get used to it, but your
programming will improve significantly after awhile and
you will be free to make different mistakes.
Does the script compile?
You can check for compilation errors by using the -c
switch. Concentrate on the first errors reported. Rinse,
repeat. If you are getting really strange errors, check to
ensure that your script has the right line endings. If you
FTP in binary mode, checkout from CVS, or something else that
does not handle line end translation, the web server may see
your script as one big line. Transfer Perl scripts in ASCII
mode.
Is the script complaining about insecure dependencies?
If your script complains about insecure dependencies, you
are probably using the -T switch to turn on taint mode, which is
a good thing since it keeps you have passing unchecked data to the shell. If
it is complaining it is doing its job to help us write more secure scripts. Any
data originating from outside of the program (i.e. the environment)
is considered tainted. Environment variables such as PATH and
LD_LIBRARY_PATH
are particularly troublesome. You have to set these to a safe value
or unset them completely, as I recommend. You should be using absolute
paths anyway. If taint checking complains about something else,
make sure that you have untainted the data. See perlsec
man page for details.
What happens when you run it from the command line?
Does the script output what you expect when run from the
command line? Is the header output first, followed by a
blank line? Remember that STDERR may be merged with STDOUT
if you are on a terminal (e.g. an interactive session), and
due to buffering may show up in a jumbled order. Turn on
Perl's autoflush feature by setting $| to a
true value. Typically you might see $|++; in
CGI programs. Once set, every print and write will
immediately go to the output rather than being buffered.
You have to set this for each filehandle. Use select to
change the default filehandle, like so:
$|++; #sets $| for STDOUT
$old_handle = select( STDERR ); #change to STDERR
$|++; #sets $| for STDERR
select( $old_handle ); #change back to STDOUT
Either way, the first thing output should be the CGI header
followed by a blank line.
What happens when you run it from the command line with a CGI-like environment?
The web server environment is usually a lot more limited
than your command line environment, and has extra
information about the request. If your script runs fine
from the command line, you might try simulating a web server
environment. If the problem appears, you have an
environment problem.
Unset or remove these variables
PATH
LD_LIBRARY_PATH
all ORACLE_* variables
Set these variables
REQUEST_METHOD (set to GET, HEAD, or POST as appropriate)
SERVER_PORT (set to 80, usually)
REMOTE_USER (if you are doing protected access stuff)
Recent versions of CGI.pm ( > 2.75 ) require the -debug flag to
get the old (useful) behavior, so you might have to add it to
your CGI.pm imports.
use CGI qw(-debug)
Are you using die() or warn?
Those functions print to STDERR unless you have redefined
them. They don't output a CGI header, either. You can get
the same functionality with packages such as CGI::Carp
What happens after you clear the browser cache?
If you think your script is doing the right thing, and
when you perform the request manually you get the right
output, the browser might be the culprit. Clear the cache
and set the cache size to zero while testing. Remember that
some browsers are really stupid and won't actually reload
new content even though you tell it to do so. This is
especially prevalent in cases where the URL path is the
same, but the content changes (e.g. dynamic images).
Is the script where you think it is?
The file system path to a script is not necessarily
directly related to the URL path to the script. Make sure
you have the right directory, even if you have to write a
short test script to test this. Furthermore, are you sure
that you are modifying the correct file? If you don't see
any effect with your changes, you might be modifying a
different file, or uploading a file to the wrong place.
(This is, by the way, my most frequent cause of such trouble
;)
Are you using CGI.pm, or a derivative of it?
If your problem is related to parsing the CGI input and you
aren't using a widely tested module like CGI.pm, CGI::Request,
CGI::Simple or CGI::Lite, use the module and get on with life.
CGI.pm has a cgi-lib.pl compatibility mode which can help you solve input
problems due to older CGI parser implementations.
Did you use absolute paths?
If you are running external commands with
system, back ticks, or other IPC facilities,
you should use an absolute path to the external program.
Not only do you know exactly what you are running, but you
avoid some security problems as well. If you are opening
files for either reading or writing, use an absolute path.
The CGI script may have a different idea about the current
directory than you do. Alternatively, you can do an
explicit chdir() to put you in the right place.
Did you check your return values?
Most Perl functions will tell you if they worked or not
and will set $! on failure. Did you check the
return value and examine $! for error messages? Did you check
$# if you were using eval?
Which version of Perl are you using?
The latest stable version of Perl is 5.28 (or not, depending on when this was last edited). Are you using an older version? Different versions of Perl may have different ideas of warnings.
Which web server are you using?
Different servers may act differently in the same
situation. The same server product may act differently with
different configurations. Include as much of this
information as you can in any request for help.
Did you check the server documentation?
Serious CGI programmers should know as much about the
server as possible - including not only the server features
and behavior, but also the local configuration. The
documentation for your server might not be available to you
if you are using a commercial product. Otherwise, the
documentation should be on your server. If it isn't, look
for it on the web.
Did you search the archives of comp.infosystems.www.authoring.cgi?
This use to be useful but all the good posters have either died or wandered off.
It's likely that someone has had your problem before,
and that someone (possibly me) has answered it in this
newsgroup. Although this newsgroup has passed its heyday, the collected wisdom from the past can sometimes be useful.
Can you reproduce the problem with a short test script?
In large systems, it may be difficult to track down a bug
since so many things are happening. Try to reproduce the problem
behavior with the shortest possible script. Knowing the problem
is most of the fix. This may be certainly time-consuming, but you
haven't found the problem yet and you're running out of options. :)
Did you decide to go see a movie?
Seriously. Sometimes we can get so wrapped up in the problem that we
develop "perceptual narrowing" (tunnel vision). Taking a break,
getting a cup of coffee, or blasting some bad guys in [Duke Nukem,Quake,Doom,Halo,COD] might give you
the fresh perspective that you need to re-approach the problem.
Have you vocalized the problem?
Seriously again. Sometimes explaining the problem aloud
leads us to our own answers. Talk to the penguin (plush toy) because
your co-workers aren't listening. If you are interested in this
as a serious debugging tool (and I do recommend it if you haven't
found the problem by now), you might also like to read The Psychology
of Computer Programming.
I think CGI::Debug is worth mentioning as well.
Are you using an error handler while you are debugging?
die statements and other fatal run-time and compile-time errors get
printed to STDERR, which can be hard to find and may be conflated with
messages from other web pages at your site. While you're debugging your
script, it's a good idea to get the fatal error messages to display in your
browser somehow.
One way to do this is to call
use CGI::Carp qw(fatalsToBrowser);
at the top of your script. That call will install a $SIG{__DIE__} handler (see perlvar)display fatal errors in your browser, prepending it with a valid header if necessary. Another CGI debugging trick that I used before I ever heard of CGI::Carp was to
use eval with the DATA and __END__ facilities on the script to catch compile-time errors:
#!/usr/bin/perl
eval join'', <DATA>;
if ($#) { print "Content-type: text/plain:\n\nError in the script:\n$#\n; }
__DATA__
# ... actual CGI script starts here
This more verbose technique has a slight advantage over CGI::Carp in that it will catch more compile-time errors.
Update: I've never used it, but it looks like CGI::Debug, as Mikael S
suggested, is also a very useful and configurable tool for this purpose.
I wonder how come no-one mentioned the PERLDB_OPTS option called RemotePort; although admittedly, there aren't many working examples on the web (RemotePort isn't even mentioned in perldebug) - and it was kinda problematic for me to come up with this one, but here it goes (it being a Linux example).
To do a proper example, first I needed something that can do a very simple simulation of a CGI web server, preferably through a single command line. After finding Simple command line web server for running cgis. (perlmonks.org), I found the IO::All - A Tiny Web Server to be applicable for this test.
Here, I'll work in the /tmp directory; the CGI script will be /tmp/test.pl (included below). Note that the IO::All server will only serve executable files in the same directory as CGI, so chmod +x test.pl is required here. So, to do the usual CGI test run, I change directory to /tmp in the terminal, and run the one-liner web server there:
$ cd /tmp
$ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
The webserver command will block in the terminal, and will otherwise start the web server locally (on 127.0.0.1 or localhost) - afterwards, I can go to a web browser, and request this address:
http://127.0.0.1:8080/test.pl
... and I should observe the prints made by test.pl being loaded - and shown - in the web browser.
Now, to debug this script with RemotePort, first we need a listener on the network, through which we will interact with the Perl debugger; we can use the command line tool netcat (nc, saw that here: Perl如何remote debug?). So, first run the netcat listener in one terminal - where it will block and wait for connections on port 7234 (which will be our debug port):
$ nc -l 7234
Then, we'd want perl to start in debug mode with RemotePort, when the test.pl has been called (even in CGI mode, through the server). This, in Linux, can be done using the following "shebang wrapper" script - which here also needs to be in /tmp, and must be made executable:
cd /tmp
cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$#'"
EOF
chmod +x perldbgcall.sh
This is kind of a tricky thing - see shell script - How can I use environment variables in my shebang? - Unix & Linux Stack Exchange. But, the trick here seems to be not to fork the perl interpreter which handles test.pl - so once we hit it, we don't exec, but instead we call perl "plainly", and basically "source" our test.pl script using do (see How do I run a Perl script from within a Perl script?).
Now that we have perldbgcall.sh in /tmp - we can change the test.pl file, so that it refers to this executable file on its shebang line (instead of the usual Perl interpreter) - here is /tmp/test.pl modified thus:
#!./perldbgcall.sh
# this is test.pl
use 5.10.1;
use warnings;
use strict;
my $b = '1';
my $a = sub { "hello $b there" };
$b = '2';
print "YEAH " . $a->() . " CMON\n";
$b = '3';
print "CMON " . &$a . " YEAH\n";
$DB::single=1; # BREAKPOINT
$b = '4';
print "STEP " . &$a . " NOW\n";
$b = '5';
print "STEP " . &$a . " AGAIN\n";
Now, both test.pl and its new shebang handler, perldbgcall.sh, are in /tmp; and we have nc listening for debug connections on port 7234 - so we can finally open another terminal window, change directory to /tmp, and run the one-liner webserver (which will listen for web connections on port 8080) there:
cd /tmp
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
After this is done, we can go to our web browser, and request the same address, http://127.0.0.1:8080/test.pl. However, now when the webserver tries to execute the script, it will do so through perldbgcall.sh shebang - which will start perl in remote debugger mode. Thus, the script execution will pause - and so the web browser will lock, waiting for data. We can now switch to the netcat terminal, and we should see the familiar Perl debugger text - however, output through nc:
$ nc -l 7234
Loading DB routines from perl5db.pl version 1.32
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(-e:1): do './test.pl'
DB<1> r
main::(./test.pl:29): $b = '4';
DB<1>
As the snippet shows, we now basically use nc as a "terminal" - so we can type r (and Enter) for "run" - and the script will run up do the breakpoint statement (see also In perl, what is the difference between $DB::single = 1 and 2?), before stopping again (note at that point, the browser will still lock).
So, now we can, say, step through the rest of test.pl, through the nc terminal:
....
main::(./test.pl:29): $b = '4';
DB<1> n
main::(./test.pl:30): print "STEP " . &$a . " NOW\n";
DB<1> n
main::(./test.pl:31): $b = '5';
DB<1> n
main::(./test.pl:32): print "STEP " . &$a . " AGAIN\n";
DB<1> n
Debugged program terminated. Use q to quit or R to restart,
use o inhibit_exit to avoid stopping after program termination,
h q, h R or h o to get additional info.
DB<1>
... however, also at this point, the browser locks and waits for data. Only after we exit the debugger with q:
DB<1> q
$
... does the browser stop locking - and finally displays the (complete) output of test.pl:
YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN
Of course, this kind of debug can be done even without running the web server - however, the neat thing here, is that we don't touch the web server at all; we trigger execution "natively" (for CGI) from a web browser - and the only change needed in the CGI script itself, is the change of shebang (and of course, the presence of the shebang wrapper script, as executable file in the same directory).
Well, hope this helps someone - I sure would have loved to have stumbled upon this, instead of writing it myself :)
Cheers!
For me, I use log4perl . It's quite useful and easy.
use Log::Log4perl qw(:easy);
Log::Log4perl->easy_init( { level => $DEBUG, file => ">>d:\\tokyo.log" } );
my $logger = Log::Log4perl::get_logger();
$logger->debug("your log message");
Honestly you can do all the fun stuff above this post.
ALTHOUGH, the simplest and most proactive solution I found was to just "print it".
In example:
(Normal code)
`$somecommand`;
To see if it's doing what I really want it to do:
(Trouble shooting)
print "$somecommand";
It will probably also be worth mentioning that Perl will always tell you on what line the error occurs when you execute the Perl script from the command line. (An SSH Session for example)
I will usually do this if all else fails. I will SSH into the server and manually execute the Perl script. For example:
% perl myscript.cgi
If there is a problem then Perl will tell you about it. This debugging method does away with any file permission related issues or web browser or web server issues.
You may run the perl cgi-script in terminal using the below command
$ perl filename.cgi
It interpret the code and provide result with HTML code.
It will report the error if any.

Perform action before save (`on_pre_save`)

import sublime_plugin
class Test(sublime_plugin.EventListener):
def on_pre_save(self, view):
view.set_syntax_file("Packages/Python/Python.tmLanguage")
Here is simple example. Logically (from my point of view), it should change syntax before saving, and so, the file should be saved as <filename>.py.
But actually, the syntax will be changed after the save operation. So, if I originally worked with js file, it will be saved as js, not py.
I'm wondering why on_pre_save works so strange, or, in other words, is there any difference between on_pre_save and on_post_save. Also, and that's my practical interest, how I can perform some arbitrary(1) action right before saving?
(1) I've specifically use the word "arbitrary", because I don't mean only syntax changes. It may be something different. For example, change the font from Consolas to Times New Roman.
The on_pre_save event happens just before a file buffer is written to disk, and allows you to take any action that you might want to take before the file on disk changes, for example making some change to the content of the buffer (e.g. "reformat on save").
The on_post_save event happens just after the file buffer has been written to disk, allowing you to take any action you might want to take after a save operation, for example examining the contents of the buffer once it's "final" (e.g. "lint on save", which if done through an external tool requires the changes to be on disk and not just in memory).
In either case the file name of the file has already been selected by the user at the time the event happens. For a new file, that means that on_pre_save doesn't happen until after they've selected the name and location of the file. For an existing file, save just resaves with the same filename.
To answer your question, you can do most any "arbitrary" thing you want in the on_pre_save to have it happen prior to when a save happens. It's also possible to change the filename in that situation if you really want to.
Note however that changing the filename out from under the user without asking them first is decidedly bad UX. Additionally, if you change the filename to a file that already exists from within on_pre_save sublime will blindly overwrite the file with no warnings, which is also Bad Mojo.
For something that's going to alter the name and location of the file on disk, the more appropriate way to go is have a command the user has to explicitly invoke to make that happen so that they're fully aware of what's going on.
As requested in a comment and for completeness, here's an example that does what you wanted your example code above to do.
The important thing to note here is that you have to be extremely careful about the situation that you trigger this event in. As written above, your plugin would make it impossible to ever save any kind of file ever due to it swapping over to a python file instead.
In this example it's constrained to only take effect on a text file, turning it into a python file. Note however that if there was a python file with that name already in that location, it would overwrite it without warning you that it's about to happen.
Be extremely wary with this code; it's quite easy to accidentally stop yourself from being able to save files with the correct name, which could for example stop you from being able to use Sublime to fix the code, amongst other nasty issues.
import sublime_plugin
import os
class TestListener(sublime_plugin.EventListener):
def on_pre_save(self, view):
# This part is extremely important because as mentioned above it's
# entirely disconcerting for your save operation to gank your
# filename and make it suddenly be something else without any
# warning. If you're not careful you might destroy your ability to
# use sublime to fix your plugin, for example.
if not view.file_name().endswith(".txt"):
print("Doing nothing for: ", view.file_name())
return
# HUGE WARNING: This CAN and WILL willfully clobber over any file
# that already happens to exist without any warning to you
# whatsoever, and is most decidedly a Bad Idea(tm)
python_name = os.path.splitext(view.file_name())[0] + ".py"
view.retarget(python_name)

Verify a Tif with ApprovalTests

I have been asked to update a system where header information gets injected into a tif via a 3rd party console application. I don't need to worry about that bit.
The part I have been asked to look at it the merge process that generates the header information.
The current file generated by the process is assumed as correct, before I make any changes, so I want to add this as an approved result, from that I can then check that the changes I make will alter the file as expected.
I thought this would be a good opportunity to look at using ApprovalTests
The problem I have is that for what ever reason the links to the videos are considered corruptible (Possibly show me kittens jumping into boxes or something, which will stop me working, which ironically means I slow down my work done because I cannot see any help videos).
What I have been looking at is the Approvals.Verify and Approvals.VerifyFile extensions.
But what appears to be happening is confusing me.
using VerifyFile creates a received file, but the contents of the file are just a line the name of the file I have asked it to verify.
using Verify(new FileInfo("FileNameHere")) does not appear to generate the received file that I need to flag as approved, but the test does return saying that it cannot find the approved tif file.
I am probably using VerifyFile completely wrong and might be looking at using Verify wrong as well.
useful info?
Might be useful to know, that as this is a legacy application, running as a windows service, I have wrapped the service in a harness that allows me to call the routines, so the files are physically being written elsewhere on the machine outside of my control (well there is a config, but the return of the service I call generates a file in a fixed location if it is successful). I have tried copying that into the Unit Test project, but that doesn't appear to help.
Verify(File) and VerifyFile(string) are both meant to verify an existing file. As such they merely setting the received file to the file you pass in. You will still need to move/approval/create the approved file.
Here is the pseudo code and process.
[UseReporter(typeof(DiffReporter), typeof(ClipboardReporter)]
public void TestTiff()
{
string tif = YourProcessToCreateTifFile();
Approvals.VerifyFile(tif);
}
[Note: if you don't have an image diff installed, like TortoiseDiff, you might want to use the FileLauncherReporter]
Run this, once you get the result, move the file over by pasting your clipboard into a cmd window.
It will move the temporary tif to your test directory with the name ClassName.TestTiff.approved.tif
After that the test should pass until something changes.
Happy Testing!

Echo a variable without executing possible malicious code in a Bash CGI binary

I'm working on a BASH CGI Binary that reads a string a user would type from an HTML form, then appends that string to a log file on my Linux server. I'm using BASH.cgi as seen on (http://oinkzwurgl.org/bash_cgi) to transfer all the POST/GET input user strings automatically into variables. Everything is working perfectly fine, however I found a major security flaw:
HTML Form...
<form action="?" method="POST">
Feedback:<textarea name="comments"></textarea>
<input type="submit" value="Submit">
</form>
BASH CGI...
echo "$comments" >> ./logs/log.txt
Suppose a malicious user would submit the following into the comments text area of the form:
$( rm -rf / )
BASH.cgi would then create a new variable called "comments" that returns the executed value of everything in between the $( and the ). This would in result compromise the server by executing anything a malicious user would please. There must be a way to directly and safely pass the string of a variable without executing what's inside. Any help would be greatly appreciated!
-- Egoscio
What you've identified is a vulnerability in Bash.CGI itself — it is failing to properly filter out $( in variables.
Do not use Bash.CGI. It is insecure. Beyond the issue you have identified, a number of other serious vulnerabilities exist in this script, some of which are listed below. I would highly recommend that you use another, more appropriate scripting language, such as Perl, Python, or PHP.
Bonus vulnerabilities:
Every variable present in a GET or POST query is imported into a variable, including variables that have special significance to the shell (e.g, PATH) and ones which were part of the CGI environment (e.g, REMOTE_USER, as noted in the documentation).
The name of a variable is not filtered at all. Variable names containing special characters may cause unexpected behavior.
Probably more. Again, this script is insecure. Do not use it.

Protect Air application content

On Mac Os, I see that all content on my application can be readable (mxml and as files).
Indeed with right clic on application, you can see all application content and so all files.
So It's very dangerous for a company to distribute air application like that.
Is a solution exist to protect those files.
Thanks
It is not possible to protect 100% your code. After all, if the computer can run it, it can be decompiled, regardless of the language. However, you can make it more difficult.
One method is to encrypt the swf as stated in another answer. But all the "attacker" needs to do is find the key and then they can decrypt all your swfs.
Another method is to use obfuscators. Obfuscators don't depend on encryption, nor they prevent decompiling, they just make it harder to understand what gets decompiled.
For example if you had a method called saveInvoice() the obfuscator would rename it to aa1() or something like that, so it would make it diffucult to guess what that function does. It basically turns everything into spaguetti code.
You can use a decompiler to see what can be obtained from a SWF file (which is alot), and play with obfuscators to see if they meet your espectations.
An example of one is http://www.kindi.com/ which I'm not endorsing btw, it just shows up quickly on google.
Although there are loads of decompilers which can read all your code. There is one guy who came up with encryption solution it might worth a try. (It's for Desktop AIR applications)
Have a look at this post: http://forums.adobe.com/message/3510525#3510525
Quoted text (in case of page being erased)
The method I use will allow you encrpyt most of your source code using
a key that is unique to every computer. The initial download of my
software is a simple air app that does not contain the actual program.
It is more like a shell that first retreaves a list of the clients mac
addresses and the user entered activation code that is created at time
of purchase. This is sent to server and logged. The activation code
is saved to a file client side. At the server the mac address and
activation key are used to create the encryption key. The bulk of the
program code is then encrypted using that key, then divided into parts
and sent back to the client. The client puts the parts back together
and saves the encrypted file. At runtime the shell finds the mac
address list and the activation key, then using same method as server
gets the encryption key and decrypts the program file. Run simple
check to make sure it loaded. For encyption i found an aes method that
works in php and javascript.
Next I use this code to load the program
var loader = air.HTMLLoader.createRootWindow(true, options, true, windowBounds);
loader.cacheResponse=false;
loader.placeLoadStringContentInApplicationSandbox=true;
loader.loadString(page);
This method makes it very difficult to copy
to another computer although since I wrote it i know there are some
weeknesses in the security but to make it harder i obv. the shell
code. It at least keeps most from pirating. However there are issues
with this that I have found. First i was using networkInfo to get the
list of mac address but this failed in a test windows XP computer.
When the wireless was off it did not return the MAC. I was not able
to recreate this in VISTA or 7. Not sure if it could happen. Was not
tested on a mac computer. To fix this (at least for windows). I
wrote a simple bat file that gets the MAC list, then converted it to
an exe which is included. This does force you to create native
installers. call the exe with this
var nativeProcessStartupInfo = new air.NativeProcessStartupInfo();
var file = air.File.applicationDirectory.resolvePath("findmac.exe");
nativeProcessStartupInfo.executable = file;
process = new air.NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(air.ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(air.ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(air.NativeProcessExitEvent.EXIT, onExit);
process.addEventListener(air.IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
process.addEventListener(air.IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
put the list together in the onOutputData event using array.push and
continue on the onExit event using the findmac.exe will return the
same info every time (that i know of) beware thought that using the
native install will break the standard application update process so
you will have to write your own. My updates are processed the same way
as above. This is contents of the .bat file to get the mac list
#Echo off
SETLOCAL SET MAC = SET Media = Connected
FOR /F "Tokens=1-2 Delims=:" %%a in ('ipconfig /all^| FIND "Physical Address"') do #echo %%b ENDLOCAL
using this method makes it simple to implement at try before you by
method. at runtime if no activation code get try me version from
server instead of full version.