JezK
Edit File: router.cls.php
<?php // phpcs:ignoreFile /** * The core plugin router class. * * This generate the valid action. * * @since 1.1.0 */ namespace LiteSpeed; defined('WPINC') || exit(); class Router extends Base { const LOG_TAG = '[Router]'; const NONCE = 'LSCWP_NONCE'; const ACTION = 'LSCWP_CTRL'; const ACTION_SAVE_SETTINGS_NETWORK = 'save-settings-network'; const ACTION_DB_OPTM = 'db_optm'; const ACTION_PLACEHOLDER = 'placeholder'; const ACTION_AVATAR = 'avatar'; const ACTION_SAVE_SETTINGS = 'save-settings'; const ACTION_CLOUD = 'cloud'; const ACTION_IMG_OPTM = 'img_optm'; const ACTION_HEALTH = 'health'; const ACTION_CRAWLER = 'crawler'; const ACTION_PURGE = 'purge'; const ACTION_CONF = 'conf'; const ACTION_ACTIVATION = 'activation'; const ACTION_CSS = 'css'; const ACTION_UCSS = 'ucss'; const ACTION_VPI = 'vpi'; const ACTION_OPTIMAX = 'optimax'; const ACTION_PRESET = 'preset'; const ACTION_IMPORT = 'import'; const ACTION_REPORT = 'report'; const ACTION_DEBUG2 = 'debug2'; const ACTION_CDN_CLOUDFLARE = 'CDN\Cloudflare'; const ACTION_ADMIN_DISPLAY = 'admin_display'; const ACTION_GUEST = 'guest'; const ACTION_MEDIA = 'media'; const ACTION_TMP_DISABLE = 'tmp_disable'; // List all handlers here private static $_HANDLERS = array( self::ACTION_ADMIN_DISPLAY, self::ACTION_ACTIVATION, self::ACTION_AVATAR, self::ACTION_CDN_CLOUDFLARE, self::ACTION_CLOUD, self::ACTION_CONF, self::ACTION_CRAWLER, self::ACTION_CSS, self::ACTION_UCSS, self::ACTION_VPI, self::ACTION_OPTIMAX, self::ACTION_DB_OPTM, self::ACTION_DEBUG2, self::ACTION_GUEST, self::ACTION_HEALTH, self::ACTION_IMG_OPTM, self::ACTION_MEDIA, self::ACTION_PRESET, self::ACTION_IMPORT, self::ACTION_PLACEHOLDER, self::ACTION_PURGE, self::ACTION_REPORT, ); const TYPE = 'litespeed_type'; const ITEM_HASH = 'hash'; const ITEM_FLASH_HASH = 'flash_hash'; private static $_esi_enabled; private static $_is_ajax; private static $_is_logged_in; private static $_ip; private static $_action; private static $_is_admin_ip; private static $_frontend_path; /** * Redirect to self to continue operation * * Note: must return when use this func. CLI/Cron call won't die in this func. * * @since 3.0 * @access public */ public static function self_redirect( $action, $type ) { if (defined('LITESPEED_CLI') || wp_doing_cron()) { Admin_Display::success('To be continued'); // Show for CLI return; } // Add i to avoid browser too many redirected warning $i = !empty($_GET['litespeed_i']) ? $_GET['litespeed_i'] : 0; ++$i; $link = Utility::build_url($action, $type, false, null, array( 'litespeed_i' => $i )); $url = html_entity_decode($link); exit("<meta http-equiv='refresh' content='0;url=$url'>"); } /** * Check if can run optimize * * @since 1.3 * @since 2.3.1 Relocated from cdn.cls * @access public */ public function can_optm() { $can = true; if (is_user_logged_in() && $this->conf(self::O_OPTM_GUEST_ONLY)) { $can = false; } elseif (is_admin()) { $can = false; } elseif (is_feed()) { $can = false; } elseif (is_preview()) { $can = false; } elseif (self::is_ajax()) { $can = false; } if (self::_is_login_page()) { Debug2::debug('[Router] Optm bypassed: login/reg page'); $can = false; } $can_final = apply_filters('litespeed_can_optm', $can); if ($can_final != $can) { Debug2::debug('[Router] Optm bypassed: filter'); } return $can_final; } /** * Check referer page to see if its from admin * * @since 2.4.2.1 * @access public */ public static function from_admin() { return !empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], get_admin_url()) === 0; } /** * Check if it can use CDN replacement * * @since 1.2.3 * @since 2.3.1 Relocated from cdn.cls * @access public */ public static function can_cdn() { $can = true; if (is_admin()) { if (!self::is_ajax()) { Debug2::debug2('[Router] CDN bypassed: is not ajax call'); $can = false; } if (self::from_admin()) { Debug2::debug2('[Router] CDN bypassed: ajax call from admin'); $can = false; } } elseif (is_feed()) { $can = false; } elseif (is_preview()) { $can = false; } /** * Bypass cron to avoid deregister jq notice `Do not deregister the <code>jquery-core</code> script in the administration area.` * * @since 2.7.2 */ if (wp_doing_cron()) { $can = false; } /** * Bypass login/reg page * * @since 1.6 */ if (self::_is_login_page()) { Debug2::debug('[Router] CDN bypassed: login/reg page'); $can = false; } /** * Bypass post/page link setting * * @since 2.9.8.5 */ $rest_prefix = function_exists('rest_get_url_prefix') ? rest_get_url_prefix() : apply_filters('rest_url_prefix', 'wp-json'); if ( !empty($_SERVER['REQUEST_URI']) && REST::str_hit_uri($_SERVER['REQUEST_URI'], [$rest_prefix . '/wp/v2/media']) && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'wp-admin') !== false ) { Debug2::debug('[Router] CDN bypassed: wp-json on admin page'); $can = false; } $can_final = apply_filters('litespeed_can_cdn', $can); if ($can_final != $can) { Debug2::debug('[Router] CDN bypassed: filter'); } return $can_final; } /** * Check if is login page or not * * @since 2.3.1 * @access protected */ protected static function _is_login_page() { if (in_array($GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ), true)) { return true; } return false; } /** * UCSS/Crawler role simulator * * @since 1.9.1 * @since 3.3 Renamed from `is_crawler_role_simulation` */ public function is_role_simulation() { if (is_admin()) { return; } if (empty($_COOKIE['litespeed_hash']) && empty($_COOKIE['litespeed_flash_hash'])) { return; } self::debug('🪪 starting role validation'); // Check if is from crawler // if ( empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) || strpos( $_SERVER[ 'HTTP_USER_AGENT' ], Crawler::FAST_USER_AGENT ) !== 0 ) { // Debug2::debug( '[Router] user agent not match' ); // return; // } $server_ip = $this->conf(self::O_SERVER_IP); if (!$server_ip || self::get_ip() !== $server_ip) { self::debug('❌❌ Role simulate uid denied! Not localhost visit!'); Control::set_nocache('Role simulate uid denied'); return; } // Flash hash validation if (!empty($_COOKIE['litespeed_flash_hash'])) { $hash_data = self::get_option(self::ITEM_FLASH_HASH, array()); if ($hash_data && is_array($hash_data) && !empty($hash_data['hash']) && !empty($hash_data['ts']) && !empty($hash_data['uid'])) { if (time() - $hash_data['ts'] < 120 && $_COOKIE['litespeed_flash_hash'] == $hash_data['hash']) { self::debug('🪪 Role simulator flash hash matched, escalating user to be uid=' . $hash_data['uid']); self::delete_option(self::ITEM_FLASH_HASH); wp_set_current_user($hash_data['uid']); return; } } } // Hash validation if (!empty($_COOKIE['litespeed_hash'])) { $hash_data = self::get_option(self::ITEM_HASH, array()); if ($hash_data && is_array($hash_data) && !empty($hash_data['hash']) && !empty($hash_data['ts']) && !empty($hash_data['uid'])) { $RUN_DURATION = $this->cls('Crawler')->get_crawler_duration(); if (time() - $hash_data['ts'] < $RUN_DURATION && $_COOKIE['litespeed_hash'] == $hash_data['hash']) { self::debug('🪪 Role simulator hash matched, escalating user to be uid=' . $hash_data['uid']); wp_set_current_user($hash_data['uid']); return; } } } self::debug('❌ WARNING: role simulator hash not match'); } /** * Get a short ttl hash (2mins) * * @since 6.4 */ public function get_flash_hash( $uid ) { $hash_data = self::get_option(self::ITEM_FLASH_HASH, array()); if ($hash_data && is_array($hash_data) && !empty($hash_data['hash']) && !empty($hash_data['ts'])) { if (time() - $hash_data['ts'] < 60) { return $hash_data['hash']; } } // Check if this user has editor access or not if (user_can($uid, 'edit_posts')) { self::debug('🛑 The user with id ' . $uid . ' has editor access, which is not allowed for the role simulator.'); return ''; } $hash = Str::rrand(32); self::update_option(self::ITEM_FLASH_HASH, array( 'hash' => $hash, 'ts' => time(), 'uid' => $uid, )); return $hash; } /** * Get a security hash * * @since 3.3 */ public function get_hash( $uid ) { // Check if this user has editor access or not if (user_can($uid, 'edit_posts')) { self::debug('🛑 The user with id ' . $uid . ' has editor access, which is not allowed for the role simulator.'); return ''; } // As this is called only when starting crawling, not per page, no need to reuse $hash = Str::rrand(32); self::update_option(self::ITEM_HASH, array( 'hash' => $hash, 'ts' => time(), 'uid' => $uid, )); return $hash; } /** * Get user role * * @since 1.6.2 */ public static function get_role( $uid = null ) { if (defined('LITESPEED_WP_ROLE')) { return LITESPEED_WP_ROLE; } if ($uid === null) { $uid = get_current_user_id(); } $role = false; if ($uid) { $user = get_userdata($uid); if (isset($user->roles) && is_array($user->roles)) { $tmp = array_values($user->roles); $role = implode(',', $tmp); // Combine for PHP5.3 const compatibility } } Debug2::debug('[Router] get_role: ' . $role); if (!$role) { return $role; // Guest user Debug2::debug('[Router] role: guest'); /** * Fix double login issue * The previous user init refactoring didn't fix this bcos this is in login process and the user role could change * * @see https://github.com/litespeedtech/lscache_wp/commit/69e7bc71d0de5cd58961bae953380b581abdc088 * @since 2.9.8 Won't assign const if in login process */ if (substr_compare(wp_login_url(), $GLOBALS['pagenow'], -strlen($GLOBALS['pagenow'])) === 0) { return $role; } } define('LITESPEED_WP_ROLE', $role); return LITESPEED_WP_ROLE; } /** * Get frontend path * * @since 1.2.2 * @access public * @return boolean */ public static function frontend_path() { // todo: move to htaccess.cls ? if (!isset(self::$_frontend_path)) { $frontend = rtrim(ABSPATH, '/'); // /home/user/public_html/frontend // get home path failed. Trac ticket #37668 (e.g. frontend:/blog backend:/wordpress) if (!$frontend) { Debug2::debug('[Router] No ABSPATH, generating from home option'); $frontend = parse_url(get_option('home')); $frontend = !empty($frontend['path']) ? $frontend['path'] : ''; $frontend = $_SERVER['DOCUMENT_ROOT'] . $frontend; } $frontend = realpath($frontend); self::$_frontend_path = $frontend; } return self::$_frontend_path; } /** * Check if ESI is enabled or not * * @since 1.2.0 * @access public * @return boolean */ public function esi_enabled() { if (!isset(self::$_esi_enabled)) { self::$_esi_enabled = defined('LITESPEED_ON') && $this->conf(self::O_ESI); if (!empty($_REQUEST[self::ACTION])) { self::$_esi_enabled = false; } } return self::$_esi_enabled; } /** * Check if crawler is enabled on server level * * @since 1.1.1 * @access public */ public static function can_crawl() { if (isset($_SERVER['X-LSCACHE']) && strpos($_SERVER['X-LSCACHE'], 'crawler') === false) { return false; } // CLI will bypass this check as crawler library can always do the 428 check if (defined('LITESPEED_CLI')) { return true; } return true; } /** * Check action * * @since 1.1.0 * @access public * @return string */ public static function get_action() { if (!isset(self::$_action)) { self::$_action = false; self::cls()->verify_action(); if (self::$_action) { defined('LSCWP_LOG') && Debug2::debug('[Router] LSCWP_CTRL verified: ' . var_export(self::$_action, true)); } } return self::$_action; } /** * Check if is logged in * * @since 1.1.3 * @access public * @return boolean */ public static function is_logged_in() { if (!isset(self::$_is_logged_in)) { self::$_is_logged_in = is_user_logged_in(); } return self::$_is_logged_in; } /** * Check if is ajax call * * @since 1.1.0 * @access public * @return boolean */ public static function is_ajax() { if (!isset(self::$_is_ajax)) { self::$_is_ajax = wp_doing_ajax(); } return self::$_is_ajax; } /** * Check if is admin ip * * @since 1.1.0 * @access public * @return boolean */ public function is_admin_ip() { if (!isset(self::$_is_admin_ip)) { $ips = $this->conf(self::O_DEBUG_IPS); self::$_is_admin_ip = $this->ip_access($ips); } return self::$_is_admin_ip; } /** * Get type value * * @since 1.6 * @access public */ public static function verify_type() { if (empty($_REQUEST[self::TYPE])) { Debug2::debug('[Router] no type', 2); return false; } Debug2::debug('[Router] parsed type: ' . $_REQUEST[self::TYPE], 2); return $_REQUEST[self::TYPE]; } /** * Check privilege and nonce for the action * * @since 1.1.0 * @access private */ private function verify_action() { if (empty($_REQUEST[self::ACTION])) { Debug2::debug2('[Router] LSCWP_CTRL bypassed empty'); return; } $action = stripslashes($_REQUEST[self::ACTION]); if (!$action) { return; } $_is_public_action = false; // Each action must have a valid nonce unless its from admin ip and is public action // Validate requests nonce (from admin logged in page or cli) if (!$this->verify_nonce($action)) { // check if it is from admin ip if (!$this->is_admin_ip()) { Debug2::debug('[Router] LSCWP_CTRL query string - did not match admin IP: ' . $action); return; } // check if it is public action if ( !in_array($action, array( Core::ACTION_QS_NOCACHE, Core::ACTION_QS_PURGE, Core::ACTION_QS_PURGE_SINGLE, Core::ACTION_QS_SHOW_HEADERS, Core::ACTION_QS_PURGE_ALL, Core::ACTION_QS_PURGE_EMPTYCACHE, )) ) { Debug2::debug('[Router] LSCWP_CTRL query string - did not match admin IP Actions: ' . $action); return; } if (apply_filters('litespeed_qs_forbidden', false)) { Debug2::debug('[Router] LSCWP_CTRL forbidden by hook litespeed_qs_forbidden'); return; } $_is_public_action = true; } /* Now it is a valid action, lets log and check the permission */ Debug2::debug('[Router] LSCWP_CTRL: ' . $action); // OK, as we want to do something magic, lets check if its allowed $_is_multisite = is_multisite(); $_is_network_admin = $_is_multisite && is_network_admin(); $_can_network_option = $_is_network_admin && current_user_can('manage_network_options'); $_can_option = current_user_can('manage_options'); switch ($action) { case self::ACTION_TMP_DISABLE: // Disable LSC for 24H Debug2::tmp_disable(); Admin::redirect("?page=litespeed-toolbox#settings-debug"); return; case self::ACTION_SAVE_SETTINGS_NETWORK: // Save network settings if ($_can_network_option) { self::$_action = $action; } return; case Core::ACTION_PURGE_BY: if (defined('LITESPEED_ON') && ($_can_network_option || $_can_option || self::is_ajax())) { // here may need more security self::$_action = $action; } return; case self::ACTION_DB_OPTM: if ($_can_network_option || $_can_option) { self::$_action = $action; } return; case Core::ACTION_PURGE_EMPTYCACHE: // todo: moved to purge.cls type action if ((defined('LITESPEED_ON') || $_is_network_admin) && ($_can_network_option || (!$_is_multisite && $_can_option))) { self::$_action = $action; } return; case Core::ACTION_QS_NOCACHE: case Core::ACTION_QS_PURGE: case Core::ACTION_QS_PURGE_SINGLE: case Core::ACTION_QS_SHOW_HEADERS: case Core::ACTION_QS_PURGE_ALL: case Core::ACTION_QS_PURGE_EMPTYCACHE: if (defined('LITESPEED_ON') && ($_is_public_action || self::is_ajax())) { self::$_action = $action; } return; case self::ACTION_ADMIN_DISPLAY: case self::ACTION_PLACEHOLDER: case self::ACTION_AVATAR: case self::ACTION_IMG_OPTM: case self::ACTION_MEDIA: case self::ACTION_CLOUD: case self::ACTION_CDN_CLOUDFLARE: case self::ACTION_CRAWLER: case self::ACTION_GUEST: case self::ACTION_PRESET: case self::ACTION_IMPORT: case self::ACTION_REPORT: case self::ACTION_CSS: case self::ACTION_UCSS: case self::ACTION_VPI: case self::ACTION_OPTIMAX: case self::ACTION_CONF: case self::ACTION_ACTIVATION: case self::ACTION_HEALTH: case self::ACTION_SAVE_SETTINGS: // Save settings if ($_can_option && !$_is_network_admin) { self::$_action = $action; } return; case self::ACTION_PURGE: case self::ACTION_DEBUG2: if ($_can_network_option || $_can_option) { self::$_action = $action; } return; case Core::ACTION_DISMISS: /** * Non ajax call can dismiss too * * @since 2.9 */ // if ( self::is_ajax() ) { self::$_action = $action; // } return; default: Debug2::debug('[Router] LSCWP_CTRL match failed: ' . $action); return; } } /** * Verify nonce * * @since 1.1.0 * @access public * @param string $action * @return bool */ public function verify_nonce( $action ) { if (!isset($_REQUEST[self::NONCE]) || !wp_verify_nonce($_REQUEST[self::NONCE], $action)) { return false; } else { return true; } } /** * Check if the ip is in the range * * @since 1.1.0 * @access public */ public function ip_access( $ip_list ) { if (!$ip_list) { return false; } if (!isset(self::$_ip)) { self::$_ip = self::get_ip(); } if (!self::$_ip) { return false; } // $uip = explode('.', $_ip); // if(empty($uip) || count($uip) != 4) Return false; // foreach($ip_list as $key => $ip) $ip_list[$key] = explode('.', trim($ip)); // foreach($ip_list as $key => $ip) { // if(count($ip) != 4) continue; // for($i = 0; $i <= 3; $i++) if($ip[$i] == '*') $ip_list[$key][$i] = $uip[$i]; // } return in_array(self::$_ip, $ip_list); } /** * Get client ip * * @since 1.1.0 * @since 1.6.5 changed to public * @access public * @return string */ public static function get_ip() { $_ip = ''; // if ( function_exists( 'apache_request_headers' ) ) { // $apache_headers = apache_request_headers(); // $_ip = ! empty( $apache_headers['True-Client-IP'] ) ? $apache_headers['True-Client-IP'] : false; // if ( ! $_ip ) { // $_ip = ! empty( $apache_headers['X-Forwarded-For'] ) ? $apache_headers['X-Forwarded-For'] : false; // $_ip = explode( ',', $_ip ); // $_ip = $_ip[ 0 ]; // } // } if (!$_ip) { $_ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false; } return $_ip; } /** * Check if opcode cache is enabled * * @since 1.8.2 * @access public */ public static function opcache_enabled() { return function_exists('opcache_reset') && ini_get('opcache.enable'); } /** * Check if opcode cache is restricted and file that is requesting. * https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.restrict-api * * @since 7.3 * @access public */ public static function opcache_restricted($file) { $restrict_value = ini_get('opcache.restrict_api'); if ($restrict_value) { if ( !$file || false === strpos($restrict_value, $file) ) { return true; } } return false; } /** * Handle static files * * @since 3.0 */ public function serve_static() { if (!empty($_SERVER['SCRIPT_URI'])) { if (strpos($_SERVER['SCRIPT_URI'], LITESPEED_STATIC_URL . '/') !== 0) { return; } $path = substr($_SERVER['SCRIPT_URI'], strlen(LITESPEED_STATIC_URL . '/')); } elseif (!empty($_SERVER['REQUEST_URI'])) { $static_path = parse_url(LITESPEED_STATIC_URL, PHP_URL_PATH) . '/'; if (strpos($_SERVER['REQUEST_URI'], $static_path) !== 0) { return; } $path = substr(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), strlen($static_path)); } else { return; } $path = explode('/', $path, 2); if (empty($path[0]) || empty($path[1])) { return; } switch ($path[0]) { case 'avatar': $this->cls('Avatar')->serve_static($path[1]); break; case 'localres': $this->cls('Localization')->serve_static($path[1]); break; default: break; } } /** * Handle all request actions from main cls * * This is different than other handlers * * @since 3.0 * @access public */ public function handler( $cls ) { if (!in_array($cls, self::$_HANDLERS)) { return; } return $this->cls($cls)->handler(); } }
BanaBridge News
https://validator.w3.org/feed/docs/rss2.html
Court Orders Ex-Weah Protocol Chief Jailed on US$8 Million Bail, Sends 15 Jurors to Prison Over Misconduct
Women Renew Call for War and Economic Crimes Court at Liberia’s Historic Peace Prayer Site
Finance Minister Ngafuan Urges Transparency, Accountability as Liberia Advances ARREST Agenda at UN Retreat
PPCC Donates ICT Equipment to 50 Government Institutions to Expand Electronic Procurement System
Liberia Commissions €100,000 Elemental Analyzer to Strengthen Environmental Research and Food Security
EPA Ends 16 Years of Renting with Purchase of Permanent Headquarters
West African Tax Bodies Deepen Partnership to Boost Revenue Mobilization, Launch Carbon Tax Study
Liberia Hosts Sierra Leone Procurement Delegation for E-GP Reform Study Tour
World Bank Praises Liberia’s Central Bank for Economic Stability and Private Sector Progress
Liberians See Slight Improvement in Corruption Fight, But Graft Still Viewed as Widespread, CENTAL Report Finds
Canada Afrique Care Foundation Set for Official Launch, Expanding Free Mental Health Access Across Canada and Africa
Weah Claims Liberia Has Become a “Narco State” as Opposition Rallies Behind Calls for Independent Probe into US$19 Million Cocaine Bust
EPA Boss Calls for Stronger Student Engagement, Unveils Push for Academic Partnership at UL Climate Symposium
Education Minister Urges Students to Embrace Tax Responsibility at National Student Tax Day
CDC Secretary General Urges U.S. Government to Review Support to Liberia National Police
Liberia Partners with CACFO to Expand Mental Health Services Ahead of Global Launch
Wreh-Toe Appointed Board Chair of CACFO as Foundation Strengthens Mental Health Push
GIABA Opens New Information Center Headquarters in Abidjan
Liberia Wins Fresh Backing as World Bank Group Executive Praises Progress, Pushes Faster Project Delivery
LRA Honors Liberia’s Top Taxpayers as Revenue Collections Hit Historic High
Root Cause in Question as Border Tensions Persist After Conakry Peace Deal
From Enforcement to Innovation: EPA Marks 2025 as a Defining Year for Liberia’s Environment
Weah Declares ‘Road to 2029 Begins Today’ as CDC Marks 22nd Anniversary in Zwedru
500 Documented Governance Failures: Activist Publishes Sweeping End-of-Year Indictment of Boakai Administration
CDC Calls for Probe into Border Crisis, Cites Alleged Mining Activities
Liberia Moves to Strengthen Small Businesses with $672,000 Packaging Solution Initiative
Former Public Works Minister Ruth Coker Collins Defends Zwedru Corridor Strategy, Calls for “Facts Over Rhetoric” in Infrastructure Debate
Power Shake-Up in CDC Diaspora: NEC Dissolves CDC-USA Interim Leadership, Appoints Rev. Muin to Lead U.S. Chapter
Tweah Reemerges, Sparks Fierce National Debate Over Governance and Liberia’s Future
Koijee Slams Tribal Politics and Political Manipulation in Lofa, Accuses Boakai Government
Mali and Burkina Faso Impose Reciprocal Travel Ban on U.S. Citizens
Guinea Election: A Turn Toward Continuity Amid Questions of Democratic Credibility
Liberia’s Revenue Grows Stronger as LRA Exceeds National Target for Second Year
Liberia’s Gender Legal Landscape: Laws in Place, Systems Lag Far Behind
Liberia’s Economy Gains Momentum as CBL Governor Highlights Progress at ECOWAS Meeting
Liberia Positions Itself for Eco Currency Entry as Economy Expands by 5.1%
From Overcrowded Classrooms to Renewal: NPA Mobilizes $1 Million to Transform UL
Ex-Public Works Minister Ruth Coker Collins Slams Ministry’s “Lack of Capacity,” Defends Her Infrastructure Record
CDC Expels Three Senior Members, Citing Alleged Alignment With Government and Undermining Party Ideology
UL to Celebrate 75 Years of Excellence
Saudi Fund Advances Liberia’s Push for Major Infrastructure Development
West African Financial Leaders Convene in Monrovia for ECOWAS Statutory Meetings
Unlocking Liberia’s Potential: Government Launches Major Revenue and Transparency Reforms
Incremental Gains, Enduring Impunity: Liberia’s CPI Improvement Fails the Test of Reform
Boakai’s Call for Unity Faces Scrutiny as Opposition, Citizens Question Actions Behind the Words
REMAPSEN Liberia Set to Strengthen Health and Environmental Reporting
Energy Sector Women Step Into the Spotlight at Liberia’s International Women’s Day Celebration
FeJAL Concludes Three-Day Retreat, Honors Outstanding Women Journalists
Dr. Rasha Kelej Named Among Africa’s 100 Most Influential Leaders
LAVA Honored for Strengthening Liberia–U.S. Ties at 2025 Military Ball