0

I want to pass my plugin a folder where it can access files, so I can run multiple instances with different folders set. Adding an attribute with the folder name was my first thought.

Because I'm using AJAX I need to access always the attribute of my shortcode. I've found this solution with passing the attribute to the JS and then pass it back when a AJAX call happens: https://wordpress.stackexchange.com/a/184219

I tried it, but it didn't worked. Is there maybe a more elegant way to do this?

Here is the important part of my PHP file:

add_action('init', 'wporg_shortcodes_init');
add_action('wp_enqueue_scripts', 'wporg_enqueue_scripts');

// My global variable for all AJAX processing PHP functions
$scrDir = "";

function wporg_shortcodes_init() {
    add_shortcode('wporg', 'wporg_shortcode');
}

function wporg_shortcode($atts = [], $content = null) {
    global $srcDir;
    $srcDir = $atts['srcdir']; // Here is the attribute set

    return '<h1>Hello World</h1>';
}

function wporg_enqueue_scripts() {
    global $srcDir; // Needs to get right context with 'global'

    wp_register_script('ajaxHandle', plugin_dir_url(__FILE__) . 'js/main.js', array(), false, true);
    wp_enqueue_script('ajaxHandle');
    wp_localize_script('ajaxHandle', 'ajax_object',
        array(
            'ajaxurl' => admin_url('admin-ajax.php'),
            'srcdir' => $srcDir));
}

This is my AJAX call from main.js, which passes the attribute back:

$.ajax({
    url: ajax_object.ajaxurl,
    type: 'POST',
    data:{
        action: 'search_for_files',
        data: 'Test data',
        srcdir: ajax_object.srcdir // Pass attribute back
    },
    success: function(data) {
        console.log("srcdir: " + ajax_object.srcdir); // srcdir is empty although I've set it in wporg_shortcode()
    },
    dataType: "json"
});

And here is the shortshode:

[wporg srcdir="myDirectory"]

The problem is that my Javascript always returns an empty srcdir. What is wrong here?


Edit: The search expression in search_for_files with exec looks like this:

"find " . UPLOAD_FILES_PATH . $srcDir . " -iname '*" . $filename . "*' -type f";
2
  • Passing in folder names is a very dangerous thing to do as it leaves you wide open to directory traversal attacks, e.g. what if the user passes in ../../.. as the directory value? Also, shortcode attributes come from the content, and only the content. Additionally, there's no code in your question for the search_for_files ajax action, can you edit your question to include it?
    – Tom J Nowell
    Commented Jul 10, 2018 at 18:35
  • The search_for_files function searches with exec with the find shell command for a file named in data. I will add the search expression in my question.
    – DevJoe
    Commented Jul 11, 2018 at 6:27

1 Answer 1

0

The problem is ordering. $srcDir is being set correctly, but when it's set the call to wp_localize_script has already happened.

Instead, consider changing your code to refer to a data attribute in the content that the shortcode returns. This also prevents the code being tied to only allowing a single instance per page.

Additionally, there is a major security issue. By passing in the folder name, you open the site up to directory traversal attacks. Users could craft their own values that include ..\ to look anywhere in the filesystem and retrieve any file they wanted, including wp-config.php. They could then use that information to connect to your database and make steal information or insert their own information, e.g. "You've been hacked send 10,000 bitcoin to xxxxx"

Additionally, your functions and global variables are very generic, and in no way prefixed, so if another plugin used srcDir it would clash, breaking things.

I would also recommend abandoning the Admin AJAX route in favour of the easier to implement and more secure WP REST API

Have you considered using a custom post type with files attached? The same way WordPress creates posts of type attachment for uploaded files? You could have attachment files with a parent post of type document set. This would allow you to eliminate the need for an AJAX endpoint or a REST API endpoint entirely, letting you use the ones that come out of the box with WordPress at /wp-json/wp/v2/etc..

6
  • Thank you Tom. I need to list files and make them downloadable from a specific directory. In the REST API I couldn't find a way to specify a specific folder in uploads. A custom post type is not suitable, because I need to search for the files dynamically and categorize them in specific tables. The data attribute could be the solution. I will take a look on it. As I've written above I use exec with find. How can I secure this setup more up?
    – DevJoe
    Commented Jul 11, 2018 at 6:36
  • @DevJoe at an absolute minimum whitelist the possible values. With exec a user could insert syntax to end the command and start a new one of the attackers choosing
    – Tom J Nowell
    Commented Jul 11, 2018 at 7:10
  • Could you provide me more details about the data attribute that the shortcode should return? The only way I see is to access the attributes in wporg_shortcode. But as you've written the enqueue_scripts has already happended before and thus srcDir is empty. How can I access the attribute outside of wporg_shortcode?
    – DevJoe
    Commented Jul 11, 2018 at 8:15
  • you include it inside, it's a standard html data attribute e.g. <div data-foo="bar"> it isn't a WP thing
    – Tom J Nowell
    Commented Jul 11, 2018 at 12:09
  • I've fixed the empty srcDir now by moving the wp_localize_script into the wporg_shortcode function. There I can access the attributes for my plugin and provide them for my JS. It's not very clean by sending the srcDir from JS then back to PHP, but I'm glad that it's working now.
    – DevJoe
    Commented Jul 12, 2018 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.