Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package sphinx for openSUSE:Factory checked in at 2022-01-29 20:59:29 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sphinx (Old) and /work/SRC/openSUSE:Factory/.sphinx.new.1898 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sphinx" Sat Jan 29 20:59:29 2022 rev:6 rq:949725 version:2.2.11 Changes: -------- --- /work/SRC/openSUSE:Factory/sphinx/sphinx.changes 2020-07-27 17:49:26.335524991 +0200 +++ /work/SRC/openSUSE:Factory/.sphinx.new.1898/sphinx.changes 2022-01-29 21:00:04.147486015 +0100 @@ -1,0 +2,12 @@ +Thu Jan 27 20:00:41 UTC 2022 - Bruno Friedmann <br...@ioda-net.ch> + +- Added CVE-2020-29050.patch for fixing CVE-2020-29050 boo#1195227 + Source debian and manticoresearch +- Packaging: + + Renew with spec-cleaner -m + + Update copyright year + + Cleanup older suse version %if construct + + Use libmariadb-devel instead mysql-devel + + Use %autopatch + +------------------------------------------------------------------- New: ---- CVE-2020-29050.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sphinx.spec ++++++ --- /var/tmp/diff_new_pack.Dcu4u5/_old 2022-01-29 21:00:04.671482492 +0100 +++ /var/tmp/diff_new_pack.Dcu4u5/_new 2022-01-29 21:00:04.675482466 +0100 @@ -1,7 +1,7 @@ # # spec file for package sphinx # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -23,13 +23,6 @@ %global soname 0_0_1 # For being able to build for SLE_11 %{!?_tmpfilesdir:%global _tmpfilesdir %{_prefix}/lib/tmpfiles.d} -%if 0%{?suse_version} > 1210 -%define has_systemd 1 -BuildRequires: systemd-rpm-macros -%{?systemd_ordering} -%else -Requires(pre): %insserv_prereq -%endif Name: sphinx Version: 2.2.11 Release: 0 @@ -43,11 +36,14 @@ Patch0: obs.patch Patch2: sphinx-default_listen.patch Patch3: reproducible.patch +#CVE-2020-29050 https://salsa.debian.org/debian/sphinxsearch/-/blob/4d6fe40644130308604845db43d3588e715ec85d/debian/patches/06-CVE-2020-29050.patch +Patch4: CVE-2020-29050.patch # for fix-ups BuildRequires: dos2unix BuildRequires: gcc-c++ -BuildRequires: mysql-devel +BuildRequires: libmariadb-devel BuildRequires: pkgconfig +BuildRequires: systemd-rpm-macros BuildRequires: pkgconfig(expat) BuildRequires: pkgconfig(libecpg) >= 9.6 BuildRequires: pkgconfig(libecpg_compat) >= 9.6 @@ -57,16 +53,8 @@ Requires(pre): %{_bindir}/getent Requires(pre): %{_sbindir}/useradd Provides: %{daemon} -%if 0%{?suse_version} +%{?systemd_ordering} Requires(post): %fillup_prereq -#Requires(pre): permissions >= 2014.11 -%else -Requires(post): /sbin/chkconfig -Requires(postun): /sbin/service -Requires(preun): /sbin/chkconfig -Requires(preun): /sbin/service -%endif -# %description Sphinx is a standalone search engine providing size-efficient and @@ -100,15 +88,13 @@ Pure C searchd client API library Sphinx search engine, http://sphinxsearch.com/ + # Comment # we don't package api language java,ruby,php,python # upstream don't recommend their usage. - %prep %setup -q -n "%{name}-%{version}-release" -%patch0 -p1 -%patch2 -p1 -%patch3 -p1 +%autopatch -p1 find -type d -name CVS -exec rm -Rf {} + @@ -179,76 +165,34 @@ } EOF -%if 0%{?has_systemd} # Create /usr/tempfiles.d/sphinx mkdir -p %{buildroot}%{_tmpfilesdir} cat > %{buildroot}%{_tmpfilesdir}/%{name}.conf << EOF d /run/%{name} 755 sphinx root - EOF -%endif # systemd vs SysVinit mkdir -p %{buildroot}%{_sbindir} -%if 0%{?has_systemd} - install -D -m 644 %{SOURCE1} %{buildroot}%{_unitdir}/%{daemon}.service - ln -sf %{_sbindir}/service %{buildroot}%{_sbindir}/rc%{daemon} -%else -#SysVinit - install -D %{SOURCE2} %{buildroot}%{_sysconfdir}/init.d/%{daemon} - %if 0%{?suse_version} - ln -sf %{_sysconfdir}/init.d/%{daemon} %{buildroot}%{_sbindir}/rc%{daemon} - %endif -%endif +install -D -m 644 %{SOURCE1} %{buildroot}%{_unitdir}/%{daemon}.service +ln -sf %{_sbindir}/service %{buildroot}%{_sbindir}/rc%{daemon} %pre getent group %{sphinx_group} >/dev/null || groupadd -r %{sphinx_group} getent passwd %{sphinx_user} >/dev/null || \ useradd -r -g %{sphinx_group} -d %{sphinx_home} -s /bin/sh \ -c "Sphinx Searchd daemon" %{sphinx_user} -%if 0%{?has_systemd} %service_add_pre %{daemon}.service -%endif %post -%if 0%{?has_systemd} %service_add_post %{daemon}.service %{_bindir}/systemd-tmpfiles --create %{_tmpfilesdir}/%{name}.conf || true -%else - %if 0%{?suse_version} -%{fillup_and_insserv -n "%{daemon}"} - %else - /sbin/chkconfig --add %{daemon} - %endif -%endif %preun -%if 0%{?has_systemd} %service_del_preun %{daemon}.service %{_bindir}/systemd-tmpfiles --remove %{_tmpfilesdir}/%{name}.conf || true -%else - %if 0%{?suse_version} -%stop_on_removal %{daemon} - %else - if [ $1 = 0 ] ; then - service %{daemon} stop >/dev/null 2>&1 - /sbin/chkconfig --del %{daemon} - fi - %endif -%endif %postun -%if 0%{?has_systemd} %service_del_postun %{daemon}.service -%else - %if 0%{?suse_version} -%restart_on_update %{daemon} -%insserv_cleanup - %else - if [ "$1" -ge "1" ] ; then - service %{daemon} restart >/dev/null 2>&1 - fi - %endif -%endif %post -n libsphinxclient-%{soname} -p /sbin/ldconfig %postun -n libsphinxclient-%{soname} -p /sbin/ldconfig @@ -262,13 +206,9 @@ %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf %config %{_sysconfdir}/%{name}/%{name}.conf.dist %config %{_sysconfdir}/%{name}/%{name}-min.conf.dist -%if 0%{?has_systemd} %{_unitdir}/%{daemon}.service %{_tmpfilesdir}/%{name}.conf %ghost /run/%{name} -%else -%{_sysconfdir}/init.d/%{daemon} -%endif %{_sbindir}/rc%{daemon} %config %{_sysconfdir}/logrotate.d/%{name} %attr(755,root,root) %{_bindir}/spelldump @@ -284,17 +224,11 @@ %doc %attr(644, root, man) %{_mandir}/man1/searchd.1* %doc %attr(644, root, man) %{_mandir}/man1/spelldump.1* %dir %attr(0750, root, %{sphinx_group}) %{_localstatedir}/log/%{name} -#Doesn't work on SLE_11 :-( -%if 0%{?suse_version} > 1230 %ghost %attr(0640, %{sphinx_user}, root) %{_localstatedir}/log/%{name}/%{daemon}.log %ghost %attr(0640, %{sphinx_user}, root) %{_localstatedir}/log/%{name}/query.log -%endif %dir %attr(0755, %{sphinx_user}, %{sphinx_group}) %{_localstatedir}/lib/%{name} %dir %attr(0755, %{sphinx_user}, %{sphinx_group}) %{_localstatedir}/lib/%{name}/data %dir %attr(0755, %{sphinx_user}, %{sphinx_group}) %{_localstatedir}/lib/%{name}/data/index -%dir %attr(0750, root, root) %{_localstatedir}/log/%{name} -%attr(0640, %{sphinx_user}, root) %{_localstatedir}/log/%{name}/%{daemon}.log -%attr(0640, %{sphinx_user}, root) %{_localstatedir}/log/%{name}/query.log %files -n libsphinxclient-%{soname} %license COPYING ++++++ CVE-2020-29050.patch ++++++ >From 17245f7dd75d47c7365797b3d5feba8976367c6e Mon Sep 17 00:00:00 2001 From: klirichek <ale...@manticoresearch.com> Date: Tue, 2 Jul 2019 19:06:09 +0700 Subject: [PATCH] Fix random file reading by scattered snippets Issue was that load_files_scattered option just concatenated prefix with given name and then opened the file. So, if you provide something like '/etc/password' as file, or '../../../etc/passwd' even with non-zero prefix, it will unfortunately work. After this commit such behavior possible ONLY if user explicitly set `snippets_file_prefix` to empty string or '/'; another cases will not cause uncontroled reading. That fixes #866. [basilgello: Back-port to 2.2.11: - Refactor sphNormalizePath to use only available classes. - Do not use 'g_sSnippetsFilePrefix' in src/sphinxexcerpt.h, use 'tOptions.m_sFilePrefix' instead, as for 2.2.11, all two instances are assigned to 'g_sSnippetsFilePrefix'. - Adjust context. ] The following test passes, adapted from commit 66b5761ad258c60b1866a8e1333f86e74f48035 at https://github.com/manticoresoftware/manticoresearch : ==== /* cd test && g++ -o test test.cpp -I../src -I../config -I/usr/include/postgresql \ -I/usr/include/mariadb -DHAVE_CONFIG_H -L../src -lsphinx -pthread \ -lm -ldl -lstemmer -lmariadb -lpq -lexpat -lz */ void ASSERT_STREQ(const char *src, const char *dst) { auto normalized = sphNormalizePath(src); if (!strcmp(dst, normalized.scstr())) { auto _src = src; auto _dst = dst; if (src == nullptr) _src = "nullptr"; if (dst == nullptr) _dst = "nullptr"; std::cout << "OK : '" << _src << "' = '" << _dst << "'" << std::endl; } else { auto _src = src; auto _dst = dst; if (src == nullptr) _src = "nullptr"; if (dst == nullptr) _dst = "nullptr"; std::cout << "FAIL: '" << _src << "' != '" << _dst << "' ( = '" << normalized.scstr() << "')" << std::endl; } } int main() { ASSERT_STREQ ( "/", "/" ); ASSERT_STREQ ( "/..//bbb", "/bbb" ); ASSERT_STREQ ( "/quite/long/path/../../../etc/passwd", "/etc/passwd" ); ASSERT_STREQ ( "/aaa/bbb/ccc/ddd/../../../../../../../", "/" ); ASSERT_STREQ ( "", "" ); ASSERT_STREQ ( nullptr, "" ); ASSERT_STREQ ( "aaa/", "aaa" ); ASSERT_STREQ ( "aaa/.", "aaa" ); ASSERT_STREQ ( "aaa/././././////././", "aaa" ); ASSERT_STREQ ( "aaa/////", "aaa" ); ASSERT_STREQ ( "aaa/bbb/ccc", "aaa/bbb/ccc" ); ASSERT_STREQ ( "aaa/bbb/ccc/ddd/..", "aaa/bbb/ccc" ); ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../..", "aaa" ); ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../xxx", "aaa/xxx" ); ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../..", "" ); ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../../", "" ); ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../../../../../", "../../.." ); ASSERT_STREQ ( "..//bbb", "../bbb" ); return 0; } ==== Fixes CVE-2020-29050. Signed-off-by: Vasyl Gello <vasek.ge...@gmail.com> --- doc/sphinx.html | 19 ++++++++++- doc/sphinx.txt | 19 ++++++++++- sphinx.conf.in | 4 +-- src/searchd.cpp | 8 ++++- src/sphinx.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++ src/sphinxexcerpt.cpp | 14 +++++++++ src/sphinxexcerpt.h | 3 ++ src/sphinxstd.h | 3 ++ test/test_130/test.xml | 7 +++-- 9 files changed, 140 insertions(+), 8 deletions(-) diff --git a/doc/sphinx.html b/doc/sphinx.html index b56e445..8ad44dd 100644 --- a/doc/sphinx.html +++ b/doc/sphinx.html @@ -11652,7 +11652,7 @@ binlog_max_log_size = 16M <div class="sect2" title="12.4.28. snippets_file_prefix"><div class="titlepage"><div><div><h3 class="title"><a name="conf-snippets-file-prefix"></a>12.4.28. snippets_file_prefix</h3></div></div></div> <p> A prefix to prepend to the local file names when generating snippets. -Optional, default is empty. +Optional, default is current working folder. Introduced in version 2.1.1-beta. </p><p> This prefix can be used in distributed snippets generation along with @@ -11663,6 +11663,19 @@ is set to "server1" and the request refers to "file23", <code class="filename">s will attempt to open "server1file23" (all of that without quotes). So if you need it to be a path, you have to mention the trailing slash. </p><p> +After constructing final file path, daemon unwinds all relative dirs and +compares final result with the value of ``snippet_file_prefix``. If result +is not begin with the prefix, such file will be rejected with error message. + +So, if you set it to '/mnt/data' and somebody calls snippet generation with file +'../../../etc/passwd', as the source, it will get error message +`File '/mnt/data/../../../etc/passwd' escapes '/mnt/data/' scope` +instead of content of the file. + +Also, with non-set parameter and reading '/etc/passwd' it will actually read +/daemon/working/folder/etc/passwd since default for param is exactly daemon's +working folder. +</p><p> Note also that this is a local option, it does not affect the agents anyhow. So you can safely set a prefix on a master server. The requests routed to the agents will not be affected by the master's setting. They will however be affected @@ -11673,6 +11686,10 @@ This might be useful, for instance, when the document storage locations </p><h4><a name="idp33320288"></a>Example:</h4><pre class="programlisting"> snippets_file_prefix = /mnt/common/server1/ </pre></div> +<p><span class="bold"><strong>WARNING:</strong></span> +If you still want to access files from the FS root, you have to explicitly set +'snippets_file_prefix' to empty value (by 'snippets_file_prefix=' line), or to +root (by 'snippets_file_prefix=/'). <div class="sect2" title="12.4.29. collation_server"><div class="titlepage"><div><div><h3 class="title"><a name="conf-collation-server"></a>12.4.29. collation_server</h3></div></div></div> <p> Default server collation. diff --git a/doc/sphinx.txt b/doc/sphinx.txt index ed994f9..f750f4e 100644 --- a/doc/sphinx.txt +++ b/doc/sphinx.txt @@ -12832,7 +12832,7 @@ Example: ----------------------------- A prefix to prepend to the local file names when generating snippets. -Optional, default is empty. Introduced in version 2.1.1-beta. +Optional, default is current working folder. Introduced in version 2.1.1-beta. This prefix can be used in distributed snippets generation along with load_files or load_files_scattered options. @@ -12842,6 +12842,19 @@ to "server1" and the request refers to "file23", searchd will attempt to open "server1file23" (all of that without quotes). So if you need it to be a path, you have to mention the trailing slash. +After constructing final file path, daemon unwinds all relative dirs and +compares final result with the value of ``snippet_file_prefix``. If result +is not begin with the prefix, such file will be rejected with error message. + +So, if you set it to '/mnt/data' and somebody calls snippet generation with file +'../../../etc/passwd', as the source, it will get error message +`File '/mnt/data/../../../etc/passwd' escapes '/mnt/data/' scope` +instead of content of the file. + +Also, with non-set parameter and reading '/etc/passwd' it will actually read +/daemon/working/folder/etc/passwd since default for param is exactly daemon's +working folder. + Note also that this is a local option, it does not affect the agents anyhow. So you can safely set a prefix on a master server. The requests routed to the agents will not be affected by the master's setting. They @@ -12855,6 +12868,10 @@ Example: | snippets_file_prefix = /mnt/common/server1/ +WARNING: If you still want to access files from the FS root, you have to +explicitly set 'snippets_file_prefix' to empty value (by 'snippets_file_prefix=' +line), or to root (by 'snippets_file_prefix=/'). + 12.4.29. collation_server ------------------------- diff --git a/sphinx.conf.in b/sphinx.conf.in index 6ba2dc9..527bbd8 100644 --- a/sphinx.conf.in +++ b/sphinx.conf.in @@ -604,7 +604,7 @@ index test1 # snippet document file name prefix # preprended to file names when generating snippets using load_files option # WARNING, this is a prefix (not a path), trailing slash matters! - # optional, default is empty + # optional, default is current working directory of a running process # # snippets_file_prefix = /mnt/mydocs/server1 @@ -1042,7 +1042,7 @@ searchd # a prefix to prepend to the local file names when creating snippets # with load_files and/or load_files_scatter options - # optional, default is empty + # optional, default is current working directory of a running process # # snippets_file_prefix = /mnt/common/server1/ } diff --git a/src/searchd.cpp b/src/searchd.cpp index 85b1cd6..6462b16 100644 --- a/src/searchd.cpp +++ b/src/searchd.cpp @@ -13330,6 +13330,12 @@ bool MakeSnippets ( CSphString sIndex, CSphVector<ExcerptQuery_t> & dQueries, CS struct stat st; CSphString sFilename; sFilename.SetSprintf ( "%s%s", g_sSnippetsFilePrefix.cstr(), dQueries[i].m_sSource.cstr() ); + if ( !TestEscaping ( g_sSnippetsFilePrefix, sFilename )) + { + sError.SetSprintf( "File '%s' escapes '%s' scope", + sFilename.scstr(), g_sSnippetsFilePrefix.scstr()); + return false; + } if ( ::stat ( sFilename.cstr(), &st )<0 ) { if ( !bScattered ) @@ -23719,7 +23725,7 @@ int WINAPI ServiceMain ( int argc, char **argv ) if ( hSearchd.Exists ( "snippets_file_prefix" ) ) g_sSnippetsFilePrefix = hSearchd["snippets_file_prefix"].cstr(); else - g_sSnippetsFilePrefix = ""; + g_sSnippetsFilePrefix.SetSprintf("%s/", sphGetCwd().scstr()); const char* sLogFormat = hSearchd.GetStr ( "query_log_format", "plain" ); if ( !strcmp ( sLogFormat, "sphinxql" ) ) diff --git a/src/sphinx.cpp b/src/sphinx.cpp index d6a7b9d..492c908 100644 --- a/src/sphinx.cpp +++ b/src/sphinx.cpp @@ -81,9 +81,11 @@ #include <io.h> // for open() // workaround Windows quirks + #include <direct.h> #define popen _popen #define pclose _pclose #define snprintf _snprintf + #define getcwd _getcwd #define sphSeek _lseeki64 #define stat _stat64 @@ -12420,6 +12422,75 @@ static bool sphTruncate ( int iFD ) #endif } +CSphString sphNormalizePath( const CSphString& sOrigPath ) +{ + CSphVector<CSphString> dChunks; + const char* sBegin = sOrigPath.scstr(); + const char* sEnd = sBegin + sOrigPath.Length(); + const char* sPath = sBegin; + int iLevel = 0; + + while ( sPath<sEnd ) + { + const char* sSlash = ( char* ) memchr( sPath, '/', sEnd - sPath ); + if ( !sSlash ) + sSlash = sEnd; + + auto iChunkLen = sSlash - sPath; + + switch ( iChunkLen ) + { + case 0: // empty chunk skipped + ++sPath; + continue; + case 1: // simple dot chunk skipped + if ( *sPath=='.' ) + { + sPath += 2; + continue; + } + break; + case 2: // double dot abandons chunks, then decrease level + if ( sPath[0]=='.' && sPath[1]=='.' ) + { + if ( dChunks.GetLength() <= 0 ) + --iLevel; + else + dChunks.Pop(); + sPath += 3; + continue; + } + default: break; + } + CSphString temp( "" ); + temp.SetBinary( sPath, iChunkLen ); + dChunks.Add( temp ); + sPath = sSlash + 1; + } + + CSphStringBuilder sResult; + if ( *sBegin=='/' ) + sResult += "/"; + else + while ( iLevel++<0 ) + dChunks.Insert(0, ".."); + + int i; + for ( i=0; i<dChunks.GetLength(); i++ ) { + sResult += dChunks[i].scstr(); + if (i<dChunks.GetLength()-1) + sResult += "/"; + } + + return sResult.cstr(); +} + +CSphString sphGetCwd() +{ + CSphVector<char> sBuf (65536); + return getcwd( sBuf.Begin(), sBuf.GetLength()); +} + class DeleteOnFail : public ISphNoncopyable { public: diff --git a/src/sphinxexcerpt.cpp b/src/sphinxexcerpt.cpp index b42c6a2..ff593f0 100644 --- a/src/sphinxexcerpt.cpp +++ b/src/sphinxexcerpt.cpp @@ -3817,6 +3817,11 @@ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, cons { CSphString sFilename; sFilename.SetSprintf ( "%s%s", tOptions.m_sFilePrefix.cstr(), tOptions.m_sSource.cstr() ); + if ( !TestEscaping( tOptions.m_sFilePrefix.scstr(), sFilename )) + { + sError.SetSprintf( "File '%s' escapes '%s' scope", sFilename.scstr(), tOptions.m_sFilePrefix.scstr()); + return; + } if ( tFile.Open ( sFilename.cstr(), SPH_O_READ, sError )<0 ) return; } else if ( tOptions.m_sSource.IsEmpty() ) @@ -3859,6 +3864,15 @@ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, cons sWarning, sError, pQueryTokenizer, tOptions.m_dRes ); } +// check whether filepath from sPath does not escape area of sPrefix +bool TestEscaping( const CSphString& sPrefix, const CSphString& sPath ) +{ + if ( sPrefix.IsEmpty() || sPrefix==sPath ) + return true; + auto sNormalized = sphNormalizePath( sPath ); + return sPrefix==sNormalized.SubString( 0, sPrefix.Length()); +} + // // $Id$ // diff --git a/src/sphinxexcerpt.h b/src/sphinxexcerpt.h index cf48b1f..87a55d4 100644 --- a/src/sphinxexcerpt.h +++ b/src/sphinxexcerpt.h @@ -81,6 +81,9 @@ struct XQQuery_t; void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, const CSphHTMLStripper * pStripper, const XQQuery_t & tExtQuery, DWORD eExtQuerySPZ, CSphString & sWarning, CSphString & sError, CSphDict * pDict, ISphTokenizer * pDocTokenizer, ISphTokenizer * pQueryTokenizer ); +// helper whether filepath from sPath does not escape area of sPrefix +bool TestEscaping( const CSphString& sPrefix, const CSphString& sPath ); + #endif // _sphinxexcerpt_ // diff --git a/src/sphinxstd.h b/src/sphinxstd.h index 39cc7ee..c1f15c1 100644 --- a/src/sphinxstd.h +++ b/src/sphinxstd.h @@ -2294,6 +2294,9 @@ int sphOpenFile ( const char * sFile, CSphString & sError ); /// return size of file descriptor int64_t sphGetFileSize ( int iFD, CSphString & sError ); +// unwind different tricks like "../../../etc/passwd" +CSphString sphNormalizePath ( const CSphString& sOrigPath ); +CSphString sphGetCwd(); /// buffer trait that neither own buffer nor clean-up it on destroy template < typename T > diff --git a/test/test_130/test.xml b/test/test_130/test.xml index d8f746c..41e4961 100644 --- a/test/test_130/test.xml +++ b/test/test_130/test.xml @@ -7,6 +7,7 @@ searchd { <searchd_settings/> + snippets_file_prefix=<this_test/>/ } source test @@ -30,15 +31,15 @@ index test $results = array(); -$docs = array( 'test_130/load_file.txt' ); +$docs = array( "load_file.txt" ); $opts = array( 'load_files'=>true, 'limit'=>0 ); $results[] = $client->BuildExcerpts($docs, 'test', 'end point', $opts ); $results[] = $client->BuildExcerpts($docs, 'test', 'not_found', $opts ); -$results[] = $client->BuildExcerpts(array( 'test_130/empty.txt' ), 'test', 'end point', $opts ); +$results[] = $client->BuildExcerpts(array( 'empty.txt' ), 'test', 'end point', $opts ); $results[] = $client->BuildExcerpts(array( '' ), 'test', 'not_found', $opts ); $results[] = $client->GetLastError(); -$results[] = $client->BuildExcerpts ( array ( 'test_130/512k.xml' ), 'test', 'it builds', array ( "limit" => 100, "load_files" => true ) ); +$results[] = $client->BuildExcerpts ( array ( '512k.xml' ), 'test', 'it builds', array ( "limit" => 100, "load_files" => true ) ); ]]></custom_test> -- 2.17.1