store all the errors occurred on eval() in PHP - eval

Stack community.
I'm using the eval() function in PHP so my users can execute his own code in my website (Yes, i know it is a dangerous function, but that's not the point).
I want to store all the PHP errors that occur during the interpretation of the code, is there a way to fetch all of them? i want to get and register them in a table of my database.
The error_get_last gets only the last error, but i want all of them.
Help me, please. It is even possible?

General
You cannot use eval() for this, as the evaled code will run in the current context, meaning that the evaled code can overwrite all vars in your context. Beside from security considerations this could / would break functionality. Check this imaginal example:
$mode = 'execute'
// here comes a common code example, it will overwrite `$mode`
eval('
$mode = 'test';
if(....) { ...
');
// here comes your code again, will fail
switch ( $mode) {
...
}
Error Tracking
You cannot track the errors this way. One method would be to use set_error_handler() to register a custom error handler which stores the errors to db. This would work, but what if the user uses the function in it's code? Check the following examples:
set_error_handler('my_handler');
function my_handler($errno, $errstr, $errfile, $errline) {
db_save($errstr, ...);
}
eval('
$a = 1 / 0; // will trigger a warning
echo $b; // variable not defined
'
);
This would work. But problems will arise if have an evaled code like this:
eval('
restore_error_handler();
$a = 1 / 0; // will trigger a warning
echo $b; // variable not defined
'
);
Solution
A common solution to make it possible that others can execute code on your servers is:
store user code into temporary file
disable critical functions like fopen() ... in the php.ini
execute the temporary php file by php-cli and display output (and errors) to the user
if you separate stdin from stdout when calling the php-cli, you can parse the error messages and store them in a DB

According to the documentation, you just can't :
If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally. It is not possible to catch a parse error in eval() using set_error_handler().
EDIT: you can't do it with eval(), but you apparently can with php_check_syntax function. You have to write the code to a file in order to check its syntax.

Related

How to get the file name of the failed switch branch in libgit2?

I can call the switch branch interface normally, but when the switch branch fails, I cannot get the specific file of the current branch failure. Viewing error info only shows "one or more conflict prevents checkout", if I want to get the detailed error file name, How to get detailed error information from the callback function or return value? (also include:Merge态Reset...)
// code
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
git_branch_lookup(&lookup, repo, branchname, GIT_BRANCH_LOCAL);
git_revparse_single(&treeish, repo, branchbane);
if(git_checkout_tree(repo, treeish, &opts)<0)
{
/*
just return "1 conflict prevents checkout",
But I want to know which files is wrong
*/
const git_error* error = giterr_last();
}
You'll need to set a notify_cb in your git_checkout_options, and set your notify_flags to include GIT_CHECKOUT_NOTIFY_CONFLICT .
Your notify callback that you provide will be invoked with the files that are changed in your working directory and preventing the checkout from occurring.
I am not so familiar with libgit2 but it looks like you'll have to solve the conflict manually. The error that you get :
1 conflict prevents checkout
Tells you that there is a file with a conflict, but you'll have to iterate through your tree to find which one and to solve it.
The status example from libgit2.org would certainly be a good starting point for you.

Why does dbListTables give a warning message when called via a function? (R DBI)

I wrote a function using dbListTables from the DBI package, that throws a warning that I cannot understand. When I run the same code outside of a function, I don't get the warning message.
For info, the database used is Microsoft SQL Server.
Reproducible example
library(odbc)
library(DBI)
# dbListTables in a function: gives a warning message
dbListTablesTest <- function(dsn, userName, password){
con <- dbConnect(
odbc::odbc(),
dsn = dsn,
UID = userName,
PWD = password,
Port = 1433,
encoding = "latin1"
)
availableTables <- dbListTables(con)
}
availableTables <-
dbListTablesTest(
dsn = "myDsn"
,userName = myLogin
,password = myPassword
)
# dbListTables not within a function works fine (no warnings)
con2 <- dbConnect(
odbc::odbc(),
dsn = "myDsn",
UID = myLogin,
PWD = myPassword,
Port = 1433,
encoding = "latin1"
)
availableTables <- dbListTables(con2)
(Incidentally, I realise I should use dbDisconnect to close a connection after working with it. But that seems to throw similar warnings. So for the sake of simplicity I've omitted dbDisconnect.)
The warning message
When executing the code above, I get the following warning message when using the first option (via a function), but I do not get it when using the second option (no function).
warning messages from top-level task callback '1'
Warning message:
Could not notify connection observer. trying to get slot "info" from an object of a basic class ("character") with no slots
The warning is clearly caused by dbListTables, because it disappears when I omit that line from the above funtion.
My questions
Why am I getting this warning message?
More specifically why am I only getting it when calling dbListTables via a function?
What am I doing wrong / should I do to avoid it?
My session info
R version 3.4.2 (2017-09-28)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1
Matrix products: default
locale:
[1] LC_COLLATE=Dutch_Belgium.1252 LC_CTYPE=Dutch_Belgium.1252 LC_MONETARY=Dutch_Belgium.1252 LC_NUMERIC=C LC_TIME=Dutch_Belgium.1252
attached base packages:
[1] stats graphics grDevices utils datasets tools methods base
other attached packages:
[1] DBI_0.7 odbc_1.1.3
loaded via a namespace (and not attached):
[1] bit_1.1-12 compiler_3.4.2 hms_0.3 tibble_1.3.4 Rcpp_0.12.13 bit64_0.9-7 blob_1.1.0 rlang_0.1.2
Thanks in advance for any help!
TL:DR calling odbc::dbConnect within another function causes this warning.
After a lot of digging in the odbc github, I have found the source of the warning. Calling dbConnect creates a db connection. Within this function is the following code:
# perform the connection notification at the top level, to ensure that it's had
# a chance to get its external pointer connected, and so we can capture the
# expression that created it
if (!is.null(getOption("connectionObserver"))) { # nocov start
addTaskCallback(function(expr, ...) {
tryCatch({
if (is.call(expr) && identical(expr[[1]], as.symbol("<-"))) {
# notify if this is an assignment we can replay
on_connection_opened(eval(expr[[2]]), paste(
c("library(odbc)", deparse(expr)), collapse = "\n"))
}
}, error = function(e) {
warning("Could not notify connection observer. ", e$message, call. = FALSE)
})
# always return false so the task callback is run at most once
FALSE
})
} # nocov end
This warning call should look familiar. This is what generates the warning. So why does it do that?
The snippet above is trying to do some checking on the connection object, to see if everything went well.
How it does that, is by adding a function checking this to the 'TaskCallBack'. This is a list of functions that get executed after a top-level task is completed. I am not 100% sure on this, but from what I can tell, this means that these functions are executed after the highest function in the call stack finishes.
Normally, this would be a line in your script. So for example:
library(odbc)
con <- odbc::dbConnect(odbc::odbc(), ...)
After the assignment in the second line is finished, the following function is executed:
function(expr, ...) {
tryCatch({
if (is.call(expr) && identical(expr[[1]], as.symbol("<-"))) {
# notify if this is an assignment we can replay
on_connection_opened(eval(expr[[2]]), paste(
c("library(odbc)", deparse(expr)), collapse = "\n"))
}
}, error = function(e) {
warning("Could not notify connection observer. ", e$message, call. = FALSE)
}
}
The top-level expression gets passed to the function and used to check if the connection works. Another odbc function called on_connection_opened then does some checks. If this throws an error anywhere, the warning is given, because of the tryCatch.
So why would the function on_connection_opened crash?
The function takes the following arguments:
on_connection_opened <- function(connection, code)
and one of the first things it does is:
display_name <- connection#info$dbname
Which seems to match the warning message:
trying to get slot "info" from an object of a basic class ("character") with no slots
From the name of the argument, it is clear that the function on_connection_opened expects a database connection object in its first argument. What does it get from its caller? eval(expr[[2]])
This is the lefthand side of the original call: con
In this case, this is a connection object and everything is nice.
Now we have enough information to answer your questions:
Why am I getting this warning message?
Your function creates a connection, which queues up the check connection function. If then checks for a list of tables and returns that. The check connection function then interprets the list of tables as if it is a connection, tries to check it, and fails miserably. This throws the warning.
More specifically why am I only getting it when calling dbListTables via a function?
dbListTables is not the culprit, dbConnect is. Because you are calling it from within a function, it doesn't get the connection object it is trying to check back and fails.
What am I doing wrong / should I do to avoid it?
A workaround would be to open a connection separately and pass that into your function. This way the connection gets opened in its own call, so the check works properly.
Alternatively, you can remove the TaskCallback again:
before <- getTaskCallbackNames()
con <- odbc::dbConnect(odbc::odbc(), ...)
after <- getTaskCallbackNames()
removeTaskCallback(which(!after %in% before))
Is the running is on_connection_opened essential? What does it do exactly?
As explained by the package's creator in this comment on Github, the function handles the displaying of the connection in the connections tab in RStudio. This is not that interesting to look at if you close the connection in the same function again. So this is not essential for your function.

camel-smooks returns null in body

I am using talend-ESB and want to parse EDI message to XML using smooks & I am getting null in body. The code looks as below.
from(
"file://D:/cimt/InvoiceEDI_Mapping/" + "?noop=true"
+ "&autoCreate=true" + "&flatten=false"
+ "&fileName=InDev_EDI_Msg.txt" + "&bufferSize=128")
.routeId("TestSmooksConfig_cFile_1")
.log(org.apache.camel.LoggingLevel.WARN,
"TestSmooksConfig.cLog_1", "${body}")
.id("TestSmooksConfig_cLog_1")
.to("smooks://EDI_Config.xml")
.to("log:TestSmooksConfig.cLog_2" + "?level=WARN")
.id("TestSmooksConfig_cLog_2");
}
My Talend route looks as below.
I used following set of external dependencies.
milyn-commons-1.7.0.jar
milyn-smooks-camel-1.7.0.jar
milyn-smooks-edi-1.7.0.jar
milyn-smooks-core-1.7.0.jar
jaxen-1.1.6.jar
milyn-edisax-parser-1.4.jar
Also, I see a strange behavior that, upon execution, I still see "starting" prior to cJavaDSLProcessor, which initially made me wonder if at all it gets executed. But later, when I intentionally made a mistake in EDI-Mapping, then the route was throwing errors, which kind of convinced me that it does parse the EDI message.
I did also search before posting this question here, and found a similar problem in this link
And I tried to lower my revision of org.milyn.* jars to 1.4.0, and got an exception that the route could not register smooks component. So I continued using 1.7.0 version of org.milyn.* jars.
For the benefit of others who might bump into similar issue, I 'assume' that the output of the smooks gets written into an Object of type StringResult.class. However, in my initial implementation, there was no such option and hence the output body was null.
Later, I tried alternative approach from http://smooks.org/guide where they used processor endpoint.Actually they had even made a statement that the data could be retrieved through exports element. The below code snippet helped to fix issue.
Smooks smooks = new Smooks("edi-to-xml-smooks-config.xml");
ExecutionContext context = smooks.createExecutionContext();
smooks.setExports(new Exports(StringResult.class));
SmooksProcessor processor = new SmooksProcessor(smooks, context);
from("file://input?noop=true")
.process(processor)
.to("mock:result");

php file() triggers no Exception on valid URL

try {
$antwort = file_get_contents('http://not_existing.notnotnot', false);
if($antwort===false) echo 'ERROR';
} catch(Exception $e) {
$e->getMessage();
}
var_dump($antwort); // returns string(0) ""
I get no Exception, no false, just empty content for every URL. A valid URL returns with this snippet the right content. Why can't I get exceptions for an invalid URL?
I came to this question because a wget on the same server leads to a valid return, but with a php script I can't file() the same URL. Really weird and I have no idea how to debug it.
It won't throw an exception if the file isn't found; it will raise a warning-level error. Those are different things. From the docs:
An E_WARNING level error is generated if filename cannot be found, maxlength is less than zero, or if seeking to the specified offset in the stream fails.
You should check for a false return, as you do, and not expect to catch an exception.
Also keep in mind when fetching a URL that the remote server may return an incorrect status code (instead of the expected 404), causing your script to think the file exists when it does not. You may need to check for empty values ("") as well.
As a rule, you should avoid using file_get_contents to access files via HTTP. It's not terribly secure, and many hosts don't even allow you to use it that way. Instead, use cURL, which is specifically designed for retrieving data over the web, including via HTTP.

CakePHP Cakemenu plugin fails after global error due to incorrect string encoding

I am using CakePHP 2.1.2 with PHP 5.3.5 and a plugin called 'Cakemenu' which normally works fine. The plugin stores menus in a db table with the menu link stored as text like
array('plugin'=>null,'controller'=>'assets','action'=>'index');
The helper in the plugin gets those values, then executes this code to convert that text to an array:
//Try to evaluate the link (if starts with array)
if (eregi('^array', $value['Menu']['link'])) {
$code = "\$parse = " . $value['Menu']['link'] . ";";
$result = eval($code);
if (is_array($parse)) {
$value['Menu']['link'] = $parse;
}
}
Everything works fine unless CakePHP is handling an error. For example if I mistype the name of a controller in the browser I should get a menu and then the missing controller message. Instead I get a page full "Parse error: syntax error, unexpected $end in..." messages pointing to the line with the eval statement. If I printout the variable that is getting eval'ed I see that it has been (incorrectly) encoded with Html entities when it normally does not.
Good string to be eval'ed:
$parse = array('plugin'=>null,'controller'=>'assets','action'=>'index');
Bad string to be eval'ed:
$parse = array('plugin'=>null,'controller'=>'Parts','action'=>'add');
To temporarily fix the problem I added two statements to just replace the offending characters
$value['Menu']['link'] = str_replace( ''','\'',$value['Menu']['link']);
$value['Menu']['link'] = str_replace( '>','>',$value['Menu']['link']);
and everything works great again. Some other pieces of information that might be helpful is that the array of data used to generate the menu is read during the beforeFilter of the app and saved in a view variable and then the menu is generated as an element in the view.
I'm thinking that the error causes CakePHP (or PHP) to skip some loading or configuration process and that causes the string to be mishandled. Any help would be appreciated, thanks
Your beforeFilter() method won't be executed on error pages. You'll have to handle your errors yourself and manually call beforeFilter(). I wrote a blog post on how to use custom error pages - pay close attention to the Controller Callbacks section.

Resources