Agree, it's trivial in most cases, except that one, when there is a series of commands depending on previous ones, e.g. (not from the production code))):

set rc=$zf(-1,"[ -f /etc/environment ] && . /etc/environment && export TZ")

$zf(-1) allows to execute such series at the whole, while $zf(-100) needs to split it into parts, moving checkup logic to COS code. It's trivial as well, but ruins the idea of (semi-)formal substitution of $zf(-1) calls with $zf(-100) ones.

It seems that the docs is ambiguous here as it's not clear when one can use "" as a <null> value: in comma separated options list only, or in options array as well.

As to possible ways to exploit $zf(-1), there is some clue in ISC's announcement. It can be compromised if its arguments come from user input. Similar vulnerabilities are usually associated with dynamic SQL, not only in Caché. Other (Caché specific)  samples: Xecute, $Xecute, argument indirection. This stuff is well-known, is it a secret for anybody?
It seems that if we never use such coding style, we are safe enough. As to our company's code base, we rarely use $zf(-1), and all its usage is encapsulated in a couple of class methods.

We'll follow ISC's security recommendations, as we always do, while I don't feel myself comfortable when I don't understand the reasons of doing something. "Don't repair, if it works", as it was said by some wise man. Does it need any comment?

Using a comma-delimited list of arguments works fine, even with a null arg

Null arg is not the same as an empty string arg (""), as usual in COS. Therefore the settings of

set options(1)=""

made your first argument an empty string, and the whole command behaved as

dir "" e:\nbupg\webserver\

Didn't check your *nix version, just noticed that "NUL" should be spelled as "/dev/null".

P.S. May I ask you in turn :):

Why did you undertake this task, changing of $zf(-1) to $zf(-100), at all? Do you clearly understand the kind of treat you try to eliminate?

The solution I've found is rather simple than smart: to start a dejournalizer as a JOB with an 'answer file' specified as a principal-input device. The code prototype looks like this:  

 set fin=$zu(12)_"Temp\fin.txt"
 set fout=$zu(12)_"Temp\fout.txt"
 open fin:("NW"):1 if '$t {write "not opened!",! quit}
 use fin write "N",!,"N",!,"Y",! close fin
 job jrnrest^ztestFF("20180614.006"):(::fin:fout):1 if '$t { w "not started!" q}

Its execution resulted in "fout.txt" file like this: 

20180614.006 to 20180614.006; c:\intersystems\cache\mgr\user\ => c:\intersystems\cache\mgr\test\

Do you want to rename your journal filter? o
Do you want to delete your journal filter? o


c:\intersystems\cache\mgr\journal\20180614.006
   8.88%  14.29%  15.26%  16.79%  18.08%  19.31%  20.35%  21.32%  ....
***Journal file finished at 16:39:36
Do you want to rename your journal filter? es
Journal filter ZJRNFILT renamed to XJRNFILT


[journal operation completed]

"o" and "es" were provided by the auto-completion of "N" and "Y" answers.

Cons of this approach is that ISC can change the ^JRNRESTO dialog in some future version.
Pros: no need in reverse engineering of ^JRNREST stuff to derive a non-interactive journal restore utility.

Hope my writing will be useful for somebody besides myself. Happy coding!

Just 2c to add. If a "national" locale is effective, e.g.

%SYS>zw ^%SYS("LOCALE","CURRENT")
^%SYS("LOCALE","CURRENT")="yruw"

and the setting was done before Caché restart:

%SYS>set ^SYS("NLS","Config","LocaleFormat")=1

you will get your national day name if don't forget to set localeopt=0, e.g.

USER>f localeopt=0,1 w localeopt," ",$ZD($h,12,,,,,,,,localeopt),!
0 Среда
1 Wednesday

It's also possible to achieve the same overriding format defaults for the current process (see $ZDATE description for details). I emphasized the need in Caché restart just because didn't find it in docs.

It all depends.

Attempts to connect a port or even to connect a super-server using potentially wrong password spoil Cache Audit with nasty records, so if they are done frequently those records can easilly overfill the Audit.

Several years ago I faced the similar problem in opposite direction: how to let load balancer recognize Cache instances which are no longer alive to remove them from its list. Load balancer was to distribute super-server connections as well as web based ones. The idea of polling 1972/tcp was dropped as soon as I recognized its impact on auditting. So I used a web app which allowed unauthenticated access for the simple reason that if 57772/tcp port had reasonably answered, 1972/tcp should be accessible as well. There were no firewall(s) between the load balancer and application servers, therefore I was sure that there were no external "forces" that could prevent them from answering. The solution was deployed on a couple of different load balancers and showed its robustness on a farm of 4-7 application servers and 1000-3000 concurrent users.

Via SSH (putty, etc), am I right that you need to call csession <instance> to enter the Caché terminal? If so, there is no talk about SSH all. 

Caché has got embedded libssh2.dll/.so ages ago. Why not implement internal SSH server which can be a reasonable replacement for outdated (and Windows only) telnet one? It seems that some other projects (besides Web terminal) would take advantage from it.

During Cache backup it appears that all the available memory on the server is being used.

It's usually not a problem as the more memory is used for buffering the quicker file i/o operates. Only free memory is used for this purpose, so memory allocated by users' or system processes should not be swapped. Please add more details, why it turned to be a problem in your case?

PS. Double check in console log whether Cache allocates its shared memory segment using large pages as it guaranties that it is totally allocated at Cache startup and will neither be expanded nor swapped afterwards.

... and second thing is to insert a Close command before the correspondent Open, e.g.

..c resfile o resfile:"NWK\CP874\"

even if you are quite sure that the file is closed at the moment of opening. The reason is to avoid the cases when your program have failed with error without closing the file, so its open parameters (at least, the translation table setting) keep unchanged despite of subsequent Open command. Such cases often happen during development / debugging, when the error trap code is completely switched off or greatly simplified.

Eduard, can you explain the main difference between Sagun's method of translation table setting:

 open file:"RSK\CP874\"

and yours in this very case? The latter is a piece of code of  %Stream.FileCharacter.cls which actually sets a table:

 If (table'=0) && (i%CurrFile'="") {
   Set io=$io
   Use i%CurrFile
   $$$SETIO(table) ; -> Do $zutil(96,18,2,table)
   Use io
 }

It seems that it's some other problem, perhaps a bug.

Sagun, if you provide us with a small piece of your code where you open the file, use it, and write it, it would be easier to say something.

Hello Eduard,

Following your 1st link I've failed to find any info on 7-zip usage. It was about Libre Office stuff.

No problems with 7-zip for Windows (its native OS), not worth to mention that 7z format compression method(s) of its last versions can be unsupported (and uncompressed) with old ones. If using zip format (even with 7-zip), your are quite safe.

p7zip, its Linux branch, which I tried several years ago, performed very poor as it was not multi-threaded (as 7-zip was for years). So we decided to drop it mostly for this reason.

One little hint about 7-zip for Windows: we use its 7za.exe build in our deployment and update procedures as it doesn't need installation and can be just dropped in any place before usage.