Category Archives: javascript

Внедряем данные из spring model в js-код

Written by elwood

Часто бывает нужно передать данные с серверной части на клиентскую, причём не в виде текста, а в виде готовых объектов. К сожалению, в HTML-страницу нельзя внедрить данные напрямую, но можно сгенерировать js-скрипт, в который бы передавался JSON, готовый к употреблению. Обычно я делал это вручную, но теперь решил написать спринговый interceptor, который бы перехватывал все атрибуты model, начинающиеся с “js_”, и добавлял их на клиент автоматически.

Пример использования

@RequestMapping(value = "/create", method = RequestMethod.GET)
public String viewCreate(Model model) {
	NewsArticle article = new NewsArticle();
        // Этот атрибут будет доступен только в JSP при генерации страницы
	model.addAttribute( "article", article );
	// А этот атрибут также попадёт и в js-код в качестве атрибута глобального объекта JS_DATA
	model.addAttribute( "js_article_id", article.getId() );
	return "edit";
}
$(function() {
    var articleId = JS_DATA['js_article_id'];
    // ...
});

Код

Все необходимые кусочки файлов можно посмотреть на github, а здесь приведу только код интерсептора:

public class JsDataInjectionInterceptor extends HandlerInterceptorAdapter
{
    private final static Log log = LogFactory.getLog( JsDataInjectionInterceptor.class );
 
    @Override
    public void postHandle( HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            ModelAndView modelAndView ) throws Exception {
        //
        if (null == modelAndView) return;
 
        Map<String, Object> jsObjects = null;
        for ( Map.Entry<String, Object> entry : modelAndView.getModelMap().entrySet() ) {
            if (entry.getKey().startsWith( "js_" )) {
                if (null == jsObjects)
                    jsObjects = new HashMap<>(  );
                jsObjects.put( entry.getKey(), entry.getValue() );
            }
        }
        if (jsObjects != null) {
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable( SerializationFeature.INDENT_OUTPUT );
            String jsData = mapper.writeValueAsString( jsObjects );
            log.debug( "JsData: " + jsData );
            modelAndView.getModelMap().addAttribute( "__js_data__",
                    jsData
            );
        }
    }
}

Исследование Chrome Secure Shell

Written by elwood

Узнал о приложении Secure Shell – ssh-клиенте и эмуляторе терминала, работающем в броузере. Меня удивило, что эта штука, написанная на js, действительно работает – неужели протокол ssh с шифрованием действительно был реализован на javascript? Или он проксирует данные через внешний сервер, передавая туда сочетания клавиш, а обратно получая команды для эмулятора терминала в plain text ? При этой мысли по спине пробежал холодок. Я только что подключался к одному серверу и ввёл там правильный пароль. Если догадка верна, то мой пароль мог легко попасть к злоумышленникам. В общем, пришлось поковырять поглубже, чтобы выяснить, как всё работает.

После изучения FAQ, исходников и файлов, поставляемых с расширением, выяснено было следующее:

  • Это приложение написано в рамках проекта Chromium OS, и его исходники открыты.

  • Оно действительно написано на js, но реализации ssh в нём нет, но есть нативный плагин, встроенный в расширение при помощи технологии Native Client. В нём-то и лежит реализация ssh-протокола, а доступ к нему осуществляется через джаваскриптовое API, предоставляемое броузером Google Chrome.

  • Но механизм проксирования также присутствует ! Называется он Relay Server, и иногда работает. Из документации непонятно, при каких ситуациях используется именно этот режим. Написано что по умолчанию используется native client, но можно и специально заставить использовать relay, указав его опции в строке Relay options. Почему-то списка доступных настроек найти не удалось. По исходникам тоже непонятно, в каком случае используется native client, а в каком – relay server. Исходные коды гуглового relay сервера закрыты, в комментариях написано, что “Вы можете написать такой сервер сами, вам достаточно зареверсинжинирить такой-то js-файл”.

  • Есть джавский relay-сервер в открытых источниках: https://github.com/zyclonite/nassh-relay/
    Демосервер, на нём можно проверить работу https://relay.wsn.at/

    Заодно мы узнаём опции, которые нужно добавить в строку relay options:

    --proxy-host=relay.wsn.at --proxy-port=443 --use-ssl

    Решил проверить этот сервер, не вводя там правильного пароля – всё заработало, но строка Loading Native Client всё равно смущает. Попробовал удалить ssh_client_nl_x86_64.nexe, после этой операции Secure Shell не хотел подключаться, хотя опции для релея были.

  • Вот ещё один relay-server, здесь уже на пейтоне: https://github.com/raptium/hashi

    Выглядит очень лаконично, по сравнению с джавской реализацией. Заодно узнаём ещё один способ задания relay-server:

    To connect via the relay server, use USER@SSH_SERVER[:SSH_PORT]@RELAY_SERVER as the destination in the secure shell.

  • Native Client – очень интересная фигня. Файлы nexe, поставляемые с расширением, разные для разных процессорных платформ, но одинаковые для операционных систем. То есть ssh_client_nl_x86_64.nexe может успешно использоваться и на Windows, и на Linux, и на Mac OS X, главное чтобы система была 64-битная. Это обстоятельство меня смутило, т.к. проводя первичный осмотр в Total Commander, я обнаружил, что эти файлы начинаются с ELF, а значит, что это либо работает только на линуксах, либо где-то дальше в файле лежат нативные файлы в других форматах – PE, dylib. Такой типа самопальный fat executable. Но всё оказалось ещё интереснее. Выяснилось, что гугл в этом месте не хранит нативный готовый код, а какой-то байткод, получаемый на выходе nacl-toolchain. В сети упоминался байткод LLVM, но тут пока неясно, в чём отличие pexe от nexe. pexe не зависит от платформы, nexe-зависит. Интересно, чем они отличаются внутри. С этим пока разобраться не удалось.

    Дополнительный прикол заключаются в том, что эти бинарники безопасны, хотя и содержат нативный код. Дело в том, что они могут быть собраны только с теми библиотеками, которые портированы в nacl-ports. А верификатор броузера при загрузке этого кода проверяет, не содержит ли он потенциально небезопасных инструкций – к таким, например, относятся syscall и int.

Общий вывод

Secure Shell скорее всего безопасен для применения, но точно не ясно, при каких случаях может включаться механизм проксирующих серверов, который небезопасен по своей сути. При paranoid_mode = ON лучше либо не пользоваться этой тулзой, либо до конца разобраться в логике включения релеев.

Инструменты для отладки jQuery events

Written by elwood

Часто бывает нужно определить, какие обработчики навешаны на DOM-элементы. К сожалению, из коробки ни FireBug, ни Chrome Developer Tools не дают возможности удобно ответить на этот вопрос, так как показывают лишь прямые обработчики, которые были повешены присваиванием element.onclick = handler. Если же для этого использовался jQuery, то узнать о наличии обработчиков можно только просканировав дерево элементов до document, просматривая то, что хранится в $._data(element, ‘events’). Для этого я написал небольшой сниппет, который добавил в код сайта:

jquery-events

/**
 * Дампер jquery on- и live-обработчиков.
 * @author elwood
 * 16.10.13 15:43
 */
 
/**
 * Выводит дамп on- и live-обработчиков для указанного объекта и всех его родителей.
 * У тех обработчиков, у которых определён селектор, выводятся также дочерние узлы (относительно
 * элемента, к которому привязан обработчик), удовлетворяющие ему.
 * @param $object
 */
function dumpEvents($object) {
 
    var $current = $object;
    while ($current.length != 0) {
        console.info("processing %o", $current[0]);
        var eventsObject = $._data($current[0], 'events');
        for (var property in eventsObject) {
            console.info(property + ':');
            var handlers = eventsObject[property];
            for (var i = 0; i < handlers.length; i++) {
                if (typeof handlers[i].selector != undefined && handlers[i].selector != null) {
                    console.info("selector: " + handlers[i].selector + ", handler: %O", handlers[i].handler);
                    var $selected = $current.find(handlers[i].selector);
                    console.info("selected %d children:", $selected.length);
                    for (var j = 0; j < $selected.length; j++) {
                        console.info(">> %o", $selected[j]);
                    }
                } else {
                    console.info("handler: %O", handlers[i].handler);
                }
            }
        }
 
        $current = $current.parent();
    }
}
 
/**
 * Вариант использования с xpath (для удобства при работе в хроме).
 * @param xpath
 */
function dumpEventsX(xpath) {
    var $object = $(document).xpath(xpath);
    dumpEvents($object);
}
 
/**
 * Выводит live-обработчики, глобально привязанные к объекту document.
 */
function dumpDocumentEvents() {
    dumpEvents($(document));
}

Для работы dumpEventsX() необходимо наличие плагина jQuery.XPath. XPath я использовал для того, чтобы из панели Elements в хроме правой кнопкой можно было делать Copy XPath и потом в консоли вставлять этот xpath в качестве аргумента функции dumpEventsX().

Честно говоря, я глубоко не разбирался в том, как реализована подписка обработчиков с помощью jQuery, и есть ли другие варианты, которые работают иначе. Вроде бы, bind() и click() делают то же самое, только с конечным объектом (селектор у хендлера будет пустой, и на потомков хендлер уже не будет работать).

Параллельно с тем, как я ковырялся в этих зарослях, нашёл несколько плагинов, делающих примерно то же самое.

Для Сhrome

jQuery Debugger – в Elements появляются вкладки jQuery Data и jQuery Events для каждого DOM-элемента можно посмотреть, что к нему джикверивского привязано, очень удобное расширение

Вот как это выглядит:
jquery-debugger

Visual Event – очень красивый плагин, при нажатии на кнопку показывает все элементы с привязанными к ним событиями и кодом их обработчиков. Поддерживает события jQuery.
Event Spy – тоже полезная штука, но этот плагин, судя по всему, умеет работать только с непосредственно привязанными обработчиками. Код обработчиков тоже выводится, довольно удобно.

Для FireFox

FireQuery– аддон к FireBug, показывает данные, привязанные к DOM-элементам (правда, не очень удобная навигация) – полезный аддон
EventBug – тоже аддон к FireBug, добавляет вкладку Events к броузеру DOM-элементов
Visual Event – то же самое, что и в хроме, только для FireFox он выполнен в виде bookmarklet, то есть ссылки, которую нужно добавить в закладки, и нажимать когда работаешь со страницей. Наверное, и в Опере работает.