How can I process form data using nonce verification
-
I am working on a plugin and have tested it using Plugin Check and have resolved all of the issues reported except for one which I am not sure how to accomplish resolving it.
This is the form code I am using index.php:
<form action="" method="post"> <b>Helpers</b>: <input type="submit" name="action" value="Import" /> <input type="submit" name="action" value="Filter" /> <input type="submit" name="action" value="Translate manually" /> <input type="submit" name="action" value="Merge" /> <br /><br /> <b>Actions</b>: <input type="submit" name="action" value="Generate empty.json" /> <input style="font-weight:bold;" type="submit" name="action" value="Auto Translate" /> <br /><br /> <?php if (count($_POST) > 0) { try { if ($_POST['action'] == 'Import') { $translator->scan(); $translator->extract(); echo "<pre>"; print_r($translator->strings); echo "</pre>\n"; } if ($_POST['action'] == 'Filter') { $translator->scan(); $translator->extract(); $translator->filter(); echo "<pre>"; print_r($translator->strings); echo "</pre>\n"; } if ($_POST['action'] == 'Translate manually') { //show form $translator->prepare(); //translate if (isset($_POST['in'])) { $translation = $_POST['in']; $translator->scan(); $translator->extract(); $translator->filter(); $translator->add_translation($translation); $translator->show_merged(); } } if ($_POST['action'] == 'Merge') { $translator->merge(); } if ($_POST['action'] == 'Auto Translate') { //prepare $translator->scan(); $translator->extract(); $translator->filter(); $translator->auto_translate(); } if ($_POST['action'] == 'Generate empty.json') { //prepare $translator->scan(); $translator->extract(); $translator->filter(); $translator->save_empty(); } } catch (Exception $exc) { echo '<div style="margin-top:10px;color:red;">ERROR: ' . $exc->getMessage() . '</div>'; } } ?> </form>
The code from index.php:
Plugin Check Report:
Lines 26, 28, 33, 39, 44, 45, 54, 57 and 65: Processing form data without nonce verification.
Code I am using in the page that does the processing using translator.php:
The code from translator.php:
<?php require_once(__DIR__ . '/../config.php'); require_once(__DIR__ . '/GoogleTranslate.php'); use \Statickidz\GoogleTranslate; /** * translator scans javascript, html files, extracts strings and generate translation with json format. */ class Translator { public $files; public $strings; public $translations; /** * scan external resources * * @throws Exception */ public function scan() { global $SOURCE_DIRS; if (count($SOURCE_DIRS) == 0) throw new Exception('empty settings: $SOURCE_DIRS'); foreach ($SOURCE_DIRS as $dir) { if (strpos($dir, '\\') !== false) { //windows url $dir = str_replace('/', '\\', $dir); } $dir = realpath($dir); if (is_dir($dir)) { //directory $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)); foreach ($rii as $file) { if ($file->isDir()) { continue; } $this->files[] = $file->getPathname(); } } else if (is_file($dir)) { $this->files[] = $dir; } else { throw new Exception('can not import object: ' . $dir); } } } /** * extracts strings from files * * @throws Exception */ public function extract() { $this->strings = array(); foreach ($this->files as $file) { $content = wp_remote_get()($file); if ($content == '') throw new Exception('can not get file content: ' . $content); $strings = array(); //drop svg $content = preg_replace('|<path.*/>|i', '', $content); //drop exceptions $content = preg_replace('|\/\/no-translate BEGIN[\s\S]+?\/\/no-translate END|mi', '', $content); //drop $content = stripslashes($content); if (stripos($file, '.js') !== false) { //json $ignore_matches = [ '\.addEventListener', '\.style\.', 'aria-label', '\.font', ]; foreach ($ignore_matches as $ignore_match) { $content = preg_replace('/' . $ignore_match . '.*/', '', $content); } $content = preg_replace('|[\r\n][ \t]*//.*|', "\n", $content); //extract between ' ' $out = array(); preg_match_all("/[']([^']*)[']/", $content, $out); $strings = array_merge($strings, $out[1]); //extract between " " $out = array(); preg_match_all('/["]([^"]*)["]/', $content, $out); $strings = array_merge($strings, $out[1]); } if (stripos($file, '.htm') !== false || true) { //html $ignore_attributes = [ 'dir', 'lang', 'http-equiv', 'content', 'name', 'rel', 'style', 'onclick', 'type', 'class', 'id', 'href', 'onchange', 'onKeyUp', 'oninput', 'src', 'aria-label', ]; foreach ($ignore_attributes as $ignore_attribute) { $content = preg_replace('/' . $ignore_attribute . '="[^"]*"/', '', $content); } //extract between " " $out = array(); preg_match_all('/["]([^"]*)["]/', $content, $out); //$strings = array_merge($strings, $out[1]); //extract between > < $out = array(); preg_match_all('|>([^<]{1,200})<[^ ]|', $content, $out); $strings = array_merge($strings, $out[1]); } foreach ($strings as $string) { if (trim($string) == '' || substr($string, 0, 2) == './') continue; //remove tags $string = preg_replace('/<[^>]*>/', ' ', $string); $string = trim($string); $this->strings[] = $string; } } $this->strings = array_unique($this->strings); sort($this->strings); } /** * filters out some strings */ public function filter() { $copy = $this->strings; $this->strings = array(); foreach ($copy as $string) { $string = trim($string); if (is_numeric($string)) { //number continue; } if (strlen($string) < 2) { //too short continue; } if (preg_replace("/[^A-Z0-9]+/", "", $string[0]) == '') { //first letter must be common uppercase letter or number continue; } if (is_numeric($string[0]) && strpos($string, ' ') === false) { //if first letter is number - try to skip some continue; } if (strpos($string, '(') !== false && strpos($string, ')') !== false && strpos($string, '.') !== false) { //function, not string continue; } if (strpos($string, "\n") !== false || strpos($string, "\r") !== false) { //multi-line continue; } if (preg_replace("/[^a-z]+/", "", $string) == '') { //all caps - not translatable continue; } if (strlen($string) > 30 && strpos($string, " ") === false) { //long word without spaces continue; } $this->strings[] = $string; } $this->strings = array_unique($this->strings); $this->strings = array_values($this->strings); } /** * prepare strings for translating for user * * @throws Exception */ public function prepare() { $this->scan(); $this->extract(); $this->filter(); $data = $this->strings; $in_content = ''; if (isset($_POST['in'])) $in_content = $_POST['in']; echo '<textarea name="out" style="width:100%;height:25vh;">' . implode("\n", $data) . '</textarea><br /><br />'; echo 'Translate text above with <a >translator</a> and paste result below:<br /><br />'; echo '<textarea name="in" style="width:100%;height:25vh;">' . $in_content . '</textarea><br />'; echo '<input type="submit" name="action" value="Translate manually" />'; } /** * combines source strings and manually translated strings to json format * * @param string $translation * * @throws Exception */ public function add_translation($translation) { $translation = trim($translation); if ($translation != '') $translation = explode("\n", $translation); else $translation = array(); if (count($this->strings) == 0) throw new Exception('0 translations found in files.'); if (count($this->strings) != count($translation)) throw new Exception(count($this->strings) . ' translations imported from file, but you provided ' . count($translation) . ', it must match'); $this->translations = new stdClass(); foreach ($this->strings as $key => $value) { $translated = trim($translation[$key]); $this->translations->$value = $translated; } } /** * translates everything automatically * * @throws Exception */ public function auto_translate() { global $LANGUAGES, $LANG_DIR; $service = new GoogleTranslate(); $text = implode("\n", $this->strings); foreach ($LANGUAGES as $lang) { echo "<br />$lang: "; $file_path = $LANG_DIR . strtolower($lang) . ".json"; //read old translations $old = array(); if (file_exists($file_path)) { $old = wp_remote_get()($file_path); if ($old === false) throw new Exception('can not open file: ' . $file_path); $old = json_decode($old); if ($old === null) throw new Exception($file_path . ' data is not json'); } $translation = $service->translate('en', $lang, $text); if ($translation == '') { throw new Exception('empty response from translation service'); } $translation = str_replace("\r", '', $translation); $translation = explode("\n", $translation); if (count($this->strings) != count($translation)) { throw new Exception(count($this->strings) . ' translations imported from file, but service gave: ' . count($translation) . ', it must match'); } //generate array $this->translations = new stdClass(); foreach ($this->strings as $key => $value) { $translated = trim($translation[$key]); $this->translations->$value = $translated; } //merge $merged = (object) array_merge((array) $this->translations, (array) $old); //remove not use elements foreach ($merged as $k => $v) { if (isset($this->translations->$k) == false) { $v = null; unset($merged->$k); } } $this->translations = $merged; //generate JSON $html = wp_json_encode()($this->translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); //save $written = WP_Filesystem($file_path, $html); if ($written == 0) { throw new Exception('can not write to: ' . $file_path); } else { echo 'OK'; } //sleep 05-1s usleep(wp_rand(500, 1000) * 1000); } } /** * saves current data as empty file * * @throws Exception */ public function save_empty() { global $LANG_DIR_EMPTY; if ($LANG_DIR_EMPTY == '') return; $data = new stdClass(); foreach($this->strings as $value){ $data->$value = ''; } $data_encoded = wp_json_encode()($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); $written = WP_Filesystem($LANG_DIR_EMPTY, $data_encoded); if ($written == 0) { throw new Exception('can not write to: ' . $LANG_DIR_EMPTY); } else { echo '<p><b>File updated: <b>' . $LANG_DIR_EMPTY . '</b></p>'; } } /** * show formatted translation, use json. parameters are only for testing mode */ public function show_merged() { echo '<textarea style="width:100%;height:30vh;margin-top:10px;">'; echo wp_json_encode()($this->translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); echo '</textarea>'; } /** * merge two translations * * @throws Exception */ public function merge() { echo 'Old translations: <b>(priority on same keys)</b><br />'; $value = ''; if (isset($_POST['merge_old'])) $value = $_POST['merge_old']; echo '<textarea style="width:100%;height:20vh;" name="merge_old">' . $value . '</textarea>'; echo '<br /><br />'; echo 'New translations:<br />'; $value = ''; if (isset($_POST['merge_new'])) $value = $_POST['merge_new']; echo '<textarea style="width:100%;height:20vh;" name="merge_new">' . $value . '</textarea>'; echo '<input type="submit" name="action" value="Merge" /><br /><br />'; if (isset($_POST['merge_old']) == false) return; $old = json_decode($_POST['merge_old']); $new = json_decode($_POST['merge_new']); if ($old === null) throw new Exception('Old data is not json'); if ($new === null) throw new Exception('New data is not json'); //merge $merged = (object) array_merge((array) $new, (array) $old); //remove not use elements foreach ($merged as $k => $v) { if (isset($new->$k) == false) { $v = null; unset($merged->$k); } } echo '<textarea style="width:100%;height:30vh;">'; echo wp_json_encode()($merged, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); echo '</textarea>'; } }
Plugin Check Report:
Lines 212, 213, 366, 367, 374, 375, 379, 382 and 383: Processing form data without nonce verification.
Thank you in advance for any help that can be provided.
Viewing 3 replies - 1 through 3 (of 3 total)
Viewing 3 replies - 1 through 3 (of 3 total)
- The topic ‘How can I process form data using nonce verification’ is closed to new replies.